Go 2 is being drafted.
If you haven't heard, there will be a few major language changes coming to Go 2. The Go team has recently submitt...
For further actions, you may consider blocking this person and/or reporting abuse
Eventually you do something like this:
Which I've seen in some other examples as:
To be honest, while this proposed solution works, it still looks like we only saving a few keystrokes. It's good but not great.
It's totally different. By your way, you can stop the subsequent operations when an error occurs
That doesn't work for returning errors, though. What if I wanted to return a wrapped version of the error?
Fair enough, but in that case, the wrapping is implicit and it looks like a twisted version of a try/catch block, something that spreads confusion.
I would prefer something like this:
As its more structured, easier to read and less confusing than the draft proposal. Note that the handle could be triggered multiple times just as the original handle version.
But now there's very implicit error checking, you don't know which lines may result in an error, which is something the proposal was trying to avoid.
tldr: go maintainers stubbornly refuse to admit that nil and not having union types are their two biggest mistakes.
Mistakes that are unfixable at this point.
From the Go 2017 user survey:
Union/Sum types are never mentioned (although "types" is)... Honestly I think I can live fine without sum types
Error handling, dependency management, and generics are by far the most mentioned things, so those are what they are focusing on.
Honestly I think you have never used them before, aren't you?
Uhhh nope, can't say I've ever had the desire to use them though
I know some people really adore them, and I know they can be useful, but in Go we've got interfaces and maybe even contract-based generics soon, which honestly kinda makes it so we don't really have a large need for union types 🤷♂️
You doesn't talk like a coder. Real coder will answer "Nope, I used them" or "Indeed, I didn't use them". I don't understand you.
Are you sure about "uhhh nope"? Visitor pattern destroys readability (and performance for short unions replacement - and we actually need only short unions), other interface based approaches in Go don't provide type safety.
generics are orthogonal to to algebraic/variadic data types. They become much better with generics though and can provide full type safety for error handling (unlike current approach).
I am afraid you are actually have a little sense about the idea.
Don't waste your time arguing. Go is designed to minimize amount of computer science needed to do work. It is NOT designed for productivity or engineer happiness. It is specifically designed to make developers easily replaceable.
Hi Dean,
I feel like adding handle fallbacks is limiting my chance to spot which place exactly generated the error.
Originally we could generate different errors for various reason. The example above is actually using same messages all the time. I know we have the line number, but I still preferred the "copyfile os.open error %s" format to specify exact spot individually.
I am not saying this won't allow it, it will just look very similar.
I would love it we would have also name of function passed to handler, ie:
What do you think ?
I actually quite like this idea! I'd suggest heading over to the wiki and put it there!
Hi Dean,
I'm a little conflicted on this.
First: instead of having the pattern
var, err := method()
now the code will be "littered" withvar := check method()
. I hope they can find a way to make thatcheck
implicit. I'm still not sure how I feel about thosehandle
blocks, I've only skimmed the draft yesterday.Second: I like this reaction about the whole thing:
Third: about
handle
blocks: think about reading the code after you wrote it, maybe weeks after. You read the code, and instead of seeing how the error is handled right there you need to jump up (hopefully not many levels) to see the various conditions, then you can resume reading the code, then you see another check so you go back up with your eyes to see what happens again with the error, then you resume, then you go back up again...ps. you can add syntax highlighting to the code blocks, like:
this way:
Implicit error checking is just kinda... bad. As a quote from the draft -
In terms of that reaction, I actually like that. Once a
handle
leaves scope, it should no longer be used.I also think that
defer
should work the same way. Adefer
should execute when it leaves its scope, not when the function is done executing. It just makes more sense in my eyes and is much more intuitive.For the syntax highlighting, I used a capital "G" on accident, I'll fix that!
I meant, implicit usage of check (you can derive which functions need a check if one of the return values is
error
) but still with explicit error handling.But now, while writing this, I think I understood. If they remove the explicit
check
there's no way for the programmer to instantly figure out which method calls can trigger an error, unless they inspect the source code.Using
check
also allows the compiler to throw errors if someone forgot to addcheck
.Although I’m not a go developer I love to check out new languages and concepts they bring. I never really liked the cluttering aspect of error handling in go but this check handle pattern is really confusing.
In the examples from another comment, there were multiple handle blocks. Which one is going to be used? How can I handle two different errors in the same scope differently. Why is the handle block before the check block?
To answer your questions -
The
handle
blocks chain. Once anhandle
block is reached, it becomes part of the chain. If an error occurs, the blocks execute from the bottommost block to the topmost. (It is proposed to remove the chaining functionality, though)If you simply want to add functionality, just add another handle block when you want that functionality to start being used. (Like the file-copy example I have in the post). Otherwise you'll have to do the standard
if err != nil
There's a few reasons behind this:
a. It's easier to think about needing to "declare" your handle blocks. You can't use a handle block if it hasn't been declared yet!
b. It makes for much faster compile times to require handle blocks.
c. It makes it more similar to how
defer
works (if you don't know Go this doesn't mean much)d. Enabling a handle block to be after a check block means that the handle block may have more variables in it's scope than what was at the check block...
Thanks for your elaborate reply.
Maybe I shouldn’t do this, but in my head I compare this to the try/catch/finally concept. Catch comes after try but cannot use any variables that have not been declared before try. But it makes the flow much clearer since you usually read code from top to bottom.
And one of my questions was how to implement multiple catch blocks. With try/catch i can have muktiple of those constructs or even multiple catch blocks. Let’s say I have file operation and a parsing operation and I want to handle them differently, I could declare two different catch blocks. How would I do this with check/handle?
It would look something like this:
In current Go with the handle construct would look something like this:
Note that the
notExist, ok := err.(*os.ErrNotExist)
is how you typecast in Go. Also, a semicolon in an if statement means "just execute the left side normally and evaluate the right side as the condition", although any variables declared on the left side have block scope within the if statement.Errors in Go are just an interface, so you just cast them to the error that you want to check.
When go2 will be released?
They've said Go 2 will probably be around Go 1.15 or so. So there's still a long time, Go 2 is still in very early planning phase, and they take their time.