Skip to content

Instantly share code, notes, and snippets.

@jozef-slezak
Last active February 27, 2019 19:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jozef-slezak/93a7d9d3d18d3fce3f8c3990c031f8d0 to your computer and use it in GitHub Desktop.
Save jozef-slezak/93a7d9d3d18d3fce3f8c3990c031f8d0 to your computer and use it in GitHub Desktop.
Proposal for GO2 - use semicolons instead of check+handle

Semicolons might help to handle errors like this:

func f2() error {
  err := f1(); if err == nil { return nil }
  // ...
}

Pros:

  1. The code is very explicit (that means that every reader will understand what is happening in the code)
  2. Full control over control flow
  3. Handle error at the first place
  4. You can focus on reading function calls

Cons:

  1. Chattines (the code is chatty comparing to GO2 check keyword)

I would prefer writing more explicit code rather than beautiful code with complex control flow.

How it works: One-liner function call and simple error handling by using semicolon (instead of multiline GO1 multiline error handling)

Pros/cons of original GO2 check+handle design

Pros:

  1. Simple cases are very readable

Cons:

  1. control flow is much more complicated (much more like try+catch).
  2. it would introduce very unique / keywords
  3. idiom tends NOT to handle errors at first place but rather with some generic handler.

Suggestions

  1. keep error handling logic close to the function/method call
  2. allow reader to focus on function calls
  3. let's not add new keywords if not necessary
  4. let's keep control flow very simple

More complex example

Refactor following code snippet (I have borrowed the code snipped from golang/go#27020):

func RetryTemporaryErrors(f func() error) error {
	for {
		err := f()
		if err == nil {
			return nil
		}
		if err, ok := err.(Temporary); !(ok && err.Temporary()) {
			return Wrap(err, "not temporary error")
		}
		err = fiddleThing()
		if err != nil {
			return err
		}
		time.Sleep(10 * time.Second)
	}
}

to something like this:

func RetryTemporaryErrors(f func() error) error {
	for {
		err := f(); if err == nil { return nil } //semicolons would work not only for errors

		if !TemporaryErr(err) { 
		    return Wrap(err, "not temporary error") 
                }
		
		err = fiddleThing(); if err != nil { return Wrap(err, "fiddle unsuccessful")  }

		time.Sleep(10 * time.Second)
	}
}

Pros/cons of original GO1

I mean:

if err:= fnc1(); err != nil {
   return err
}

Pros:

  1. Full control over control flow
  2. Code is explicity

Cons:

  1. Error handling uglyfies the original code
  2. Many lines of code are dedicated to error handling
  3. The with a function call starts in this case with if but I would prefer starting with a function call
@refola
Copy link

refola commented Feb 27, 2019

I like this! Other than "don't change anything", this is the only proposal I've seen that doesn't look like weird special case magic with 40-80% of the mental overhead of Lisp macros and only 10-20% of their utility (and as someone who once tried turning Go into a Lisp for the sake of syntactic macros, "better error handling" is 25% of how I'd use them!).

Also, it looks like the only change would be in gofmt, making this fully backwards compatible with existing code.

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