-
Notifications
You must be signed in to change notification settings - Fork 14k
Description
I don't have a specific proposal, but I do want to discuss principles for how to handle partial functions in the standard libraries. Examples of partial functions are option::get (undefined on none), vec::last (undefined on empty vectors), and map::get (undefined for keys not bound in the map).
One approach is to "totalize" these functions by lifting their results into the option type: for example, vec::last would return none if invoked on an empty vector and some(x) on a non-empty vector, for some x. (That was the behavior until today.)
Another approach is to use typestate to specify preconditions so that we write total functions on a smaller domain, rather than partial functions on a bigger domain.
Finally, perhaps the simplest approach is for library functions to fail with an uncaught exception when error conditions arise.
I prefer either the first or second approach over the third (ideally I would prefer the second, but clearly there has been some hesitance to use typestate in anger). My preference is informed by my experience with Haskell where the use of functions like fromJust or head in cases where the invariants don't really hold (and the compiler doesn't help by checking those invariants) is a major pain. If you know an invariant, you should be able to write it down and express it either through types or typestate (whether it's types or typestate is more of an implementation detail to me).
However, I just changed vec::last to fail on an empty vector, for consistency with some of the existing vec code. I'd like to arrive at some guiding principles here.