> Generic methods is one place where non-explicit error handling is essential.
And, really, the error-handling part fits fairly well with Go (not only what the language supports -- which is, after all, full-featured exceptions with a different [IMO, improved] syntax -- but also with the conventions on their use across public API, though that requires some thought about the motivation for that rule and how it applies to that kind of function.)
OTOH, Go's type system, as opposed to its error-handling approach, is a real problem for generic functions. Though if you just mean non-generic higher-level functions, this is less of an issue.
> You have to make sure to thread error handling through everything that would ever take another function as an argument (since the error handling characteristics of the unknown function are open)
No, you just have to state as an assumption of the "generic" that any function it works on is wrapped to panic in the event of error, and the caller needs to handle the panics. To fit with the Go convention on panics and public APIs, you should wrap any function that results from applying such a higher-level function to a function that an fail into another function that recovers from panics and provides error returns before passing the resulting function across a public API boundary.
> OTOH, Go's type system, as opposed to its error-handling approach, is a real problem for generic functions. Though if you just mean non-generic higher-level functions, this is less of an issue.
Without generic types, I don't think generic programming is very common in Go and exceptions won't be missed much yet. However, if they ever get around to adding them, I expect dominoes to fall :)
> To fit with the Go convention on panics and public APIs, you should wrap any function that results from applying such a higher-level function to a function that an fail into another function that recovers from panics and provides error returns before passing the resulting function across a public API boundary.
So panic is basically an exception but in callback form rather than block form? Holy Hollywood principle! I'm not sure what you can hope to accomplish in a callback that would make the immediate calling context sane again.
Exceptions won't be missed at all, with or without generic programming, because, and I get tired of saying this, Go has exceptions. panic/recover/defer provide all the functionality of raise/catch/finally -- the main difference in structure being that (1) the context is always a function, not some other block within a function, and, (2) "recover" is done within a deferred function (loosely parallel to an "finally" block) as opposed to "catch" which is done in a sibling block to "finally". [1]
Defer is basically finally without block structure, triggered instead by the popping of the surrounding procedural call context. So instead of a block, you have to create a new function instead to unwind...how is the extra complexity of avoiding simple block structure worth it?
And what is the equivalent of catch if we have an equivalent of finally? It seems to be recover, which allows a deferred execution to query what exception has occurred, correct? Is there a good discussion of why this is better than try/catch/finally?
> Defer is basically finally without block structure, triggered instead by the popping of the surrounding procedural call context. So instead of a block, you have to create a new function instead to unwind...how is the extra complexity of avoiding simple block structure worth it?
How is function structure any more complex than block structure?(I mean, visually, sure, there are potentially a few extra sigils, but structurally how is a block different than a nullary function?)
> And what is the equivalent of catch if we have an equivalent of finally?
Recover within a deferred function provides the ability to do the functionality of catch -- I wouldn't call it a one-for-one equivalent (the combination of features is equivalent, but there isn't a one-for-one correspondence between the individual features except between panic and raise/throw.)
Thank you. I'd still like to know why this is better and not just different. Or is there some legacy design I'm missing, like in one of Rob Pike's previous languages?
> I'd still like to know why this is better and not just different.
Its potentially more flexible, in that it doesn't restrict the "generic" cleanup code to run strictly after the exception handling code; this also probably makes things a bit more clear in the instances where you want to apply logic in the guaranteed-closeout that might affect return values but needs to consider errors in the event they occurred.
And, really, the error-handling part fits fairly well with Go (not only what the language supports -- which is, after all, full-featured exceptions with a different [IMO, improved] syntax -- but also with the conventions on their use across public API, though that requires some thought about the motivation for that rule and how it applies to that kind of function.)
OTOH, Go's type system, as opposed to its error-handling approach, is a real problem for generic functions. Though if you just mean non-generic higher-level functions, this is less of an issue.
> You have to make sure to thread error handling through everything that would ever take another function as an argument (since the error handling characteristics of the unknown function are open)
No, you just have to state as an assumption of the "generic" that any function it works on is wrapped to panic in the event of error, and the caller needs to handle the panics. To fit with the Go convention on panics and public APIs, you should wrap any function that results from applying such a higher-level function to a function that an fail into another function that recovers from panics and provides error returns before passing the resulting function across a public API boundary.