7 ms·
This doesn't really make much sense. The producer knows what it's returning. In the _vastly_ common case, it's either returning an error or a success object, bu
by kadoban 3y ago
This doesn't really make much sense. The producer knows what it's returning. In the _vastly_ common case, it's either returning an error or a success object, but the Go type system is unable to represent that.
The caller trying to pretend that the success object is there isn't a freedom the caller gets in the current system, it's an artifact of the type system not being powerful enough to encode the situation accurately.
In practice (for the success object) it means you need to check for a nil pointer, make sure you don't use a zombie object, or just rely on an assumption that it's not nil, depending on which poor choice the producer function went for.
If you have a function that can return both an object and an error, there still should be a way to represent that (exactly the current way). Having Sum types would just allow a way to represent the common case accurately.
- randomdata 3y ago> The producer knows what it's returning. But doesn't know how the return values will be used by the caller. What is perhaps lost in this is where Go says that values should always be useful? > If you have a function that can return both an object and an error, there still should be a way to represent that (exactly the current way). Exactly the current way is what is said to be deficient, though. A function of this type is naturally going to return a file every time because a file is always useful, even when there is failure. Whereas Result assumes that you won't find the file useful when there is failure. If you know the callers you can discuss if the file will ever be useful to the callers who use it under failure condition. Always useful does not mean always used. But Go, no doubt of a product of Google's organizational structure, believes that you cannot get to know your callers. You have to give them what you've got and let them decide what and what isn't important to their specific needs. Tradeoffs, as always.
- awused 3y agoThis tries to sound profound but it really just misunderstands basic sum types. In Rust/Haskell/etc you're free to return multiple values if that's even potentially useful. If you have a function that can completely fail, partially fail (in this example, opening a file but then something else fails), or entirely succeed, you can encode that in the type system. In Go you cannot define this in the type system, you just have comments explaining how it works and hoping both that you implement it correctly and callers of your code read and understand them. If your code does return both files and errors in some cases, chances are callers are going to handle it incorrectly, especially if, say, the caller is responsible for closing files/responses/streams/etc. In Rust/Haskell I can write a type with three values like Success(file), PartialSuccess(file, error), or Failure(error). Callers must then handle all of these. In Go I always have four cases for a simple function including those three and the final case of neither file nor error. Most Go callers will not handle the case where err != nil and file != nil and often the case where err == nil and file == nil will cause a panic and crash the program. There was a tradeoff for this, but in this case the tradeoff was entirely in making the Go compiler simpler at the cost of making the Go language weaker and Go code more error-prone.
- kadoban 3y ago> But doesn't know how the return values will be used by the caller. What is perhaps lost in this is where Go says that values should always be useful? It doesn't matter how it will be used by the caller. If I'm writing a function that can fail, no magic in existence can create a success object out of nothing, especially one that "should always be useful". At that point you're stuck either returning a nil pointer or a zombie object (along with the error). > Exactly the current way is what is said to be deficient, though. It's deficient because it's modelling the wrong (in the common case) thing. I'm saying if you're in the uncommon case and that actually _is_ what you're trying to model, then you still can. > A function of this type is naturally going to return a file every time because a file is always useful, even when there is failure. What? No. It's not useful if there is no file, if the error is "wtf, that file doesn't exist".
- masklinn 3y ago> A function of this type is naturally going to return a file every time because a file is always useful, even when there is failure. You might want to look at what Go does with zero-valued File. At best it ignores them and returns an error, at worst it panics. There is no situation where a zero-valued file is useful.