
Dougal Stanton wrote:
The Maybe construction is very useful for explicitly handling circumstances where the function cannot produce a sensible answer.
But how far should this notion be taken? When you're writing a function which you realise may not produce what you want, in what circumstances would you use a Maybe, and when would you just throw an error?
It seems that if I cannot foresee how the function will be used, I need to be very general. Following the convention of Data.Map.lookup, I will use a Monad or MonadZero or MonadError. Perhaps even an ArrowZero. Every function is used in two circumstances. One circumstance is front-line code, where nobody is trusted and every invalid call is caught and handled. This is best done as above. Another circumstance is core code, where all data have been screened by the front-line code and there is no point repeating checks and handlers again and again and again. (How many times do you have to re-validate data? Take it to the extreme: If the code binds [0] to xs, do you have to check "not (null xs)" every alternate line of code? Every extra check and its associated Maybe or Monad type clutters up code, and after a threshold introduces risk of logical errors rather than reduces risk of data errors.) For core code, I may provide an unchecked version of my function, in case "just use the identity monad" is easier said than done. (It may still throw an error, if it is impossible to avoid a check linguistically, e.g., pattern matching, but at least there is no clutter from handlers or extra types, e.g., head, foldr1, etc.)