Description
Background
The feedback wiki for the Go 2 Draft Design proposing check/handle saw the emergence of two recurring themes, documented here:
https://github.com/golang/go/wiki/Go2ErrorHandlingFeedback#recurring-themes
13 responses suggested a nil test triggered by assignment, to reduce if err != nil
boilerplate:
f, ? := os.Open(...) // one of many syntaxes suggested
17 responses suggested named error handlers, to enable multiple error paths within a function:
func example(c net.Conn) error {
a := check os.Open(...) ? openErr // one of many syntaxes suggested
...
b := check os.Open(...) ? openErr
...
check io.Copy(c, a) ? copyErr
check io.Copy(c, b) ? copyErr
...
handle openErr { ... } // log error
handle copyErr { ... } // log error, write error msg to c
return nil
}
Others have newly reiterated both themes in comments on the try()
proposal #32437.
Herein is a composite of these recurring themes. I believe it bears consideration in light of the apparently deep resistance to try()
.
Summary
v, flag [op] := f()
try v, flag [op] := f() // alternative, to indicate control-flow within statement
- flag indicates that a zero-value test shall be performed on that return value,
- op gives an optional procedure to invoke on a non-zero value,
e.g. return, panic, or a named handler (defined at package-level or in current function) - try is a new keyword
- v is an ordinary variable
- := is any declaration or assignment operator
- func f() (int, error)
Possible syntax (choose one)
f, #return := os.Open(...)
f, ?return := os.Open(...)
f, @return := os.Open(...)
f, {return} := os.Open(...)
try f, # := os.Open(...)
try f, ? := os.Open(...)
try f, @ := os.Open(...)
try f, {} := os.Open(...)
Possible ops
f, #return := os.Open(...)
f, #panic := os.Open(...)
f, #hname := os.Open(...) // invoke user-defined handler
f, # := os.Open(...) // ignore, or invoke "ignored" handler if defined
try f, # := os.Open(...) // return
try f, #panic := os.Open(...)
try f, #hname := os.Open(...)
try f, #_ := os.Open(...) // ignore
Example handlers
handle fatal (err error) {
debug.PrintStack()
log.Fatal(err)
}
handle log (err error, clr caller) {
fmt.Fprintf(os.Stderr, "%s got %s, continuing\n", clr.name, err)
// may return the parent function or not, to support recoverable errors
}
// caller is a handle-specific type with results of runtime.Caller()
A sub-expression syntax (which has caused complaint in the try()
comments) is possible, but only recommended for ignore or panic ops:
f(os.Open#(...)) // ignore
f(os.Open#panic(...))
f(os.Open#p(...)) // any leading substring of panic?
f(# os.Open(...)) // prefix alternative
f(#panic os.Open(...))
f(os.Open(...) #) // postfix alternative
f(os.Open(...) #panic)
About that Pesky New Symbol
People seem to dislike #op
or @op
or ?op
, so let's ponder an alternative.
We could define three new keywords (for ops return, panic, invoke). They constrain the return value tested to the last one. To date, try
& must
have been suggested for return & panic, and we could employ check
for invoke. One can already use _
for op ignore, although it looks like a bug, and blank-assigned values can't be logged for debugging.
try f := os.Open(...) // return
must f := os.Open(...) // panic
check f := os.Open(...) else hname // invoke hname
f, _ := os.Open(...) // wait, is that a bug?
I'd rather define one new keyword (or none) and see a symbol with an op that names its action.
Work in Progress
As the try()
proposal may be adopted, I leave this proposal in an incomplete state. I may develop it further if the Go team expresses interest.
More possibilities are described in Requirements to Consider for Go 2 Error Handling, but most of them are not "recurring themes".
Who's @networkimprov? Liam is the author of
- Golang, how dare you handle my checks!
- Proposal: the #id/catch error model
- Requirements to Consider for Go 2 Error Handling (referenced in the try() design document)
- I've also done extensive gardening in the feedback wiki
https://github.com/golang/go/wiki/Go2ErrorHandlingFeedback/_history