Skip to content

Instantly share code, notes, and snippets.

@pborman
Last active August 30, 2018 00:38
Show Gist options
  • Save pborman/c69e79690d86dfc5c371f096be22930c to your computer and use it in GitHub Desktop.
Save pborman/c69e79690d86dfc5c371f096be22930c to your computer and use it in GitHub Desktop.

This is supposed to be the second file, not the first, but apparently gist did not agree. My initial comments are in the file following this one.

After reading comments from others, and thinking more on this problem, I think that the proposal goes too far and is too complicated. Anything beyond the default handler adds too much complexity for too little value. As is often said in Go, it doesn't carry its own weight. A simpler solution is to simply use named return values with defer, and if there is a check, or what ever it is called, it should make code that currently looks like:

a, b, err := foo()
if err != nil {
	return err
}

to become

a, b := check foo()

and nothing more. If you really want error handling that is more complicated than can easily be done with defer, well, Go already has you covered, just do what we have always been doing and check the error.

A take on what some others have said, it could also be:

a, b, <token> := foo()

Where is something clever. Maybe !nil to indicate it should not be nil, or just ! or !!! to show surprise there is an error, or something else that does not require a new keyword. The major point is there is no need for handle and the new behavior should be as simple as possible, e.g., a shorthand way of saying if err != nil { return err } and nothing more.

The Go 2 Error Handling proposal introduces 2 new keywords to the Go programming language, handle and catch. This means any Go 1 code that uses the names handle or catch as variable or method names (I have often used the method name handle) will no longer compile.

Despite the proposals suggestion that check is somehow different from try, for the most part it isn't. The Go 1 FAQ explains why Go does not have try-catch-finally with one of the reasons being that it leads to convoluted code. The explicit requirement to check errors as they happen in the past was a feature of Go, not a mistake.

The proposal on the board does eliminate finally, but not the try-catch part. This proposal does limit the try-catch paradigm to a function's scope, the execeptions do not unwind up the stack, which is a minor improvement over Python's try-catch, but that is not the only issue with try-catch. Take the small example in the proposal:

func main() {
	handle err {
		log.Fatal(err)
	}

	hex := check ioutil.ReadAll(os.Stdin)
	data := check parseHexdump(string(hex))
	os.Stdout.Write(data)
}

The handler does not know which of the functions generated the error. Imagine there are a dozen calls to check inside the function. The handle is applied to all of them. This is lazy code writing.

I can easily see this proposal leading to code like:

func foo() error {
	var a sometype
	var b anothertype
	{
		handle err { some handling code }
		a = check func1()
		check func2(a)
	}
	{
		handle err { some other handling code }
		b := check func3(a)
		check fmt.Printf("B is %v\n", b)
	}
	check func4(a, b)
}

This could made a bit easier if you could delete a handler:

func foo() error {
	h1 := handle err { some handling code }
	a := check func1()
	check func2(a)
	delete(h1)
	
	h2 := handle err { some other handling code }
	b := check func3(a)
	check fmt.Printf("B is %v\n", b)
	delete (h2)
	
	check func4(a, b)
}

This at least removes the extra scopes and the needing to pre-declare a and b. This syntax does have drawbacks:

func foo() error {
	h := handle err { some handling code }
	a := check func1()
	check func2(a)
	
	h = handle err { some other handling code }
	b := check func3(a)
	check fmt.Printf("B is %v\n", b)
	delete (h)
	
	check func4(a, b)
}

The second handle call makes it look like the first handler will be deleted, but of course, it will not be.

For the record, I don't like this either.

I agree that it sometimes can be a little annoying to add add two dozen extra keystrokes just to check for an error that you don't expect to really happen all that much. While I agree that it is easier to write a = check foo() rather than if a, err = foo(); err != nil { return err }, I do believe it will lead to code that does not properly handle errors.

@codekhol
Copy link

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment