5 ms·
I can't count the number of times this has forced me to make a better interface/abstraction that the one I was making. It's too bad this idiom is going to be th
by BobbyJo 5y ago
I can't count the number of times this has forced me to make a better interface/abstraction that the one I was making. It's too bad this idiom is going to be the 'wrong way' to do things soon, because, for some reason, every code Guru out there thinks number of characters/lines is the single defining cost function for software maintenance, and that's who the kids coming out of college are going to listen to.
- kadoban 5y agoIt's just fundamentally the wrong type for what it's supposed to represent. What I'd prefer isn't any more concise in lines or characters, it simply actually represents what it's supposed to. "a*, error" has 4 different members, if you consider just "exists" vs "doesn't exist" for each. Of those, "nil, nil" and "&a{}, errors.New("whatever")" are broken. Fully half of the things you can represent are just wrong. (Yes there are times when those are valid returns, but easily 99% of the time they're not. The type system should be able to represent both ways.)
- BobbyJo 5y ago> The type system should be able to represent both ways. Go's type system can already do that, the (x, error) idiom is just standard practice in the absence of Generics and Throwables. Adding generics simply flips the default and exception. Currently, the exception is to define a custom type which captures and constrains your possible return states, and the default is to return a value plus an error. When that flips, and the default is to collapse error states into a single return value, that will cease to give me the same signals (or at least make them less obvious) as to whether or not my abstraction sucks, which I will sorely miss :P
- kadoban 5y agoHow does go's type system allow the "exactly one of these two" case? What's the syntax?
- BobbyJo 5y agoI'm not sure what you're asking. I was pointing out that if you need to enforce a return result that will have either a value or an error, you can simple write a struct/interface that does that. Like, you can create an interface that's read only for the return result like: interface ReadableResult { HasResult() bool Result() whatever Error() error } Then to create the result you have a writable interface for setting values, and enforce the 'only one of the two' semantics from the setters: interface WritableResult { SetResult(whatever) SetError(error) } Struct MyResult{ err error result whatever } .... Implementation here. It's extra code, but it should be a rare necessity, at least in my experience. Res, err := getResult() always worked fine for me.
- bborud 5y agoHow can you know that half the things I can represent are wrong? There are many situations where both returning a value and not returning a value are both valid. And where errors conditions may not dictate what the returned value should be. Language design choices have to make sense. And this doesn't make practical sense to me since I've written code, just in the past 24 hours, that contradicts your assertion. And no, I don't think the code would be better if we robbed Go of expressive power - adding complexity elsewhere is just moving the problem if poor discipline around.
- kadoban 5y ago> How can you know that half the things I can represent are wrong? There are many situations where both returning a value and not returning a value are both valid. And where errors conditions may not dictate what the returned value should be. That's why I wrote: Yes there are times when those are valid returns, but easily 99% of the time they're not. The type system should be able to represent both ways. The golang type system can _already_ encode the case you're talking about, and that should stay. My complaint is that is the _only_ case it can represent, it cannot represent the more common case.
- lmm 5y ago> How can you know that half the things I can represent are wrong? There are many situations where both returning a value and not returning a value are both valid. And where errors conditions may not dictate what the returned value should be. In cases where it's valid, you use a type that makes it valid. Much of the point of having a type system is that the return type tells you exactly what the valid things for the function to return are. Having an explicit declaration of which functions might return both a result and an error and which functions will definitely return one or the other but not both makes your code easier to understand and work with. As it stands, I would bet that most Go programmers ignore the possibility of returning both a result and an error even in functions that do it deliberately, because the idiomatic thing a function with that signature does is return one or the other but not both.
- Huggernaut 5y ago