January 2018: functional errors
Meetup #45. Please RSVP at Meetup.
We get started at 6:30 pm.
Sponsors
Venue provided by Startup Edmonton.
Food sponsored by Bellstone Engineering.
Talks
Functional Options Pattern
Adam Wolfe Gordon explained the functional options pattern by first motivating it: a logging library needs many config options (destinations, level, prefix, date format) with sensible defaults, and each naive approach falls short. Multiple structs cover only one config dimension, a plain config struct leaves the zero value useless, a constructor with every parameter must change whenever a new option appears, and multiple constructors explode combinatorially. The pattern fixes this with a constructor that takes variadic option functions (func NewLogger(opts ...LoggerOpt) Logger), each a closure over the private implementation type that mutates the config; defaults are applied first and then each option. He showed it works for plain functions too (a Retry with exponential backoff) and noted real-world users like gRPC, Google Cloud, AWS, and etcd.
Key points:
- Config-rich types need good defaults plus per-option overrides.
- Naive approaches (many structs/constructors, exposed config structs) scale poorly.
- Options are functions that take and mutate a private options type.
- The constructor applies defaults, then runs each option in turn.
- Callers can't create their own options because the argument type is unexported.
- Works for constructors and ordinary functions (e.g. retry with backoff).
- Used widely: gRPC, Google Cloud, AWS, etcd.
To err is human
Nathan Youngman's talk "To err is human" was a tour of idiomatic Go error handling, starting from the observation that errors are normal and that if err != nil { return err }, far from being a nuisance, keeps the happy path unindented and makes code scannable. He dug into how errors work under the hood — the error interface, errors.New and its *errorString pointer, sentinel values like io.EOF and sql.ErrNoRows, and custom error types such as url.Error — and showed how multiple return values and defer simplify cleanup. The talk covered adding context with fmt.Errorf or pkg/errors, storing an error inside a struct so a whole operation can be written error-free, using recover only in rare cases, and passing errors over channels. He closed by simplifying if err != nil { return err }; return nil to just return fn() and by pointing to resources like "Errors are values."
Key points:
- Errors are normal;
if err != nilkeeps the happy path readable. - The
errorinterface is justError() string;errors.Newreturns a pointer sentinel. - Multiple return values and
defermake cleanup and propagation clean. - Add context with
fmt.Errorforpkg/errorsrather than losing the cause. - Treat errors as values: store one in a struct and skip work after failure.
panic/recoverare for rare cases; HTTP servers recover per request.- Errors can be passed between goroutines over channels.
Jobs
- Software Developer at Tendermint (Toronto)
News
- Go 1.9.3
- Go 1.10 beta 2
- What's new in Go 1.10
- Go Support for AWS Lambda
- Twirp RPC framework from Twitch
- Golang Weekly
- GopherAcademy Advent 2017 series
Events
- Monthly Hack Day - Jan 13
- Monthly Hack Day - Feb 3
- GopherCon - Aug 27-30
- Edmonton Go Meetup - February