Hacker News new | ask | show | jobs
by pjc50 2584 days ago
Right, so this is basically the Go approach: explicit error checking on everything. No exceptions, because exceptions are a weird sort of non-local control flow, and can escape and take down your program as a whole.

The "if you don't know the answer, find an approximation from another route" is .. situational. In this case it's exactly what the customer wants. In other cases (finance, security, aerospace) it could be a disaster waiting to happen.

I worked on a point-of-sale system where it was a matter of design philosophy that any inconsistency should be an immediate crash-and-reboot; since it also operated as a save-everywhere and audit-everywhere system, if you did manage to crash it then within 30 seconds you'd be back at the screen just before the last button press with no data loss other than the button press that caused the crash. I believe this crash-and-recover approach is very Erlang: https://ninenines.eu/articles/dont-let-it-crash/

Thinking of exceptions and message validation also makes me think of "in band" versus "out of band" signalling and errors. Exceptions are "out of band" - outside the normal function return process. Internet communication is all "in band" and the whole approach of separate control and data planes has almost entirely gone away, apart from SIP and (nearly dead) FTP.

2 comments

Sum types arguably permit a form of out-of-band return value. A function that returns a sum type has the choice of returning the usual type (an integer value, say) or an error value of some other type.

IMO this counts as a kind of out-of-band arrangement because it doesn't involve using one of the normal return values to signal error.

https://en.wikipedia.org/wiki/Semipredicate_problem

But are you actually returning either an int or an error? You're always returning a Result type which may contain either an int or an error.
In Haskell one applicable type is actually called "Either a b".

I'm not sure what your point is. If a function has a "unitary" return type (not, say, a list of possible return types out of which one is selected) then obviously it always returns a value of that type.

You could probably invent a kind of function that had a list of possible return types. Then anyone who called it would have to deal with those possibilities.

It all comes to the same thing in the end. Sum types and pattern matching are a very good system for expressing this functionality.

It's conceivable that a compiler would throw away the Either type information and just generate separate code for the different ways the function might return. Ultimately, though, I suppose it would end up calling a continuation, since the function has to "return" somehow.

I was replying to this:

> IMO this counts as a kind of out-of-band arrangement because it doesn't involve using one of the normal return values to signal error.

My point is that if you're returning an algebraic data type Result that encodes Ok(val) or NotOk(_), you are using one of the normal return values, because the function always returns a Result. The whole purpose of the construct seems to me to be bringing the error case handling into band.

The way I see it, each of the data types in a sum type is analogous to a channel. The type of the 'message' conveyed by a value is effectively "Either Control Data".

In-band signalling is messier than out-of-band signalling, so I see out-of-band as the goal. If you look at the various approaches to the semipredicate problem on that Wikipedia page, it looks like, historically, we started out with awkward in-band solutions and the evolution is towards the clarity offered by expanding the return type to send errors out-of-band.

I mentioned this previously on HN, and maybe that earlier comment will clarify what I'm talking about: https://news.ycombinator.com/item?id=18766478

pjc50's way of applying the terms to flow of control is a somewhat different thing.

Go has panics, which is essentially exceptions with a different name. Failure to handle all panics will lead to process termination, as with unhandled exceptions.
To be fair, their general use is discouraged unless you want the process to terminate. Not that you can't do that in other languages, but the culture in Go seems to prefer returning error codes.