Maybe and partial functions

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? I'm wondering both in terms of good engineering practise and also for correctness. Should elementary partial functions like
5 `div` 0
or
head []
return Nothing? I guess it's a bit of a silly suggestion, but it helps to highlight why we use Maybe in the first place. So --- where's the cutoff point in your code? Cheers, D. -- Dougal Stanton

Hi
head []
return Nothing? I guess it's a bit of a silly suggestion, but it helps to highlight why we use Maybe in the first place. So --- where's the cutoff point in your code?
If that is what you want, then see my Safe library: http://www-users.cs.york.ac.uk/~ndm/projects/libraries.php#safe headMay :: [a] -> Maybe a (note this function already exists and is called listToMaybe) However, I think that partial functions are just great, although a lot of people disagree with me. There are some tools being developed to check things like head []: Catch: http://www-users.cs.york.ac.uk/~ndm/projects/catch.php ESC/Haskell: http://www.cl.cam.ac.uk/~nx200/research/escH-hw.ps Catch also checks division by zero, explicit patterns etc. To see the world of pain you would be in if you go down the "make everything total" route, I suggest you try rewriting this program to be complete: http://darcs.haskell.org/nofib/imaginary/digits-of-e2/Main.lhs (if you do, please post the result to the list) If you ignore getArgs and read (which are incomplete in the above example), then Catch is able to prove all the remaining things complete, without annotations or code modifications. You might also want to try redoing that example in the type style, discussed here: http://www.haskell.org/haskellwiki/Non-empty_list Thanks Neil

On Mon, 12 Mar 2007 11:37:43 +0000
"Neil Mitchell"
Catch also checks division by zero, explicit patterns etc. To see the world of pain you would be in if you go down the "make everything total" route, I suggest you try rewriting this program to be complete:
http://darcs.haskell.org/nofib/imaginary/digits-of-e2/Main.lhs
(if you do, please post the result to the list)
Isn't this just an example of needing to separate data and codata, and so totality and productiveness in order to be productive in a total language? Gen

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.)

This isn't just a question about Haskell. It applies to any language
with an exception mechanism, including C++ and Java. Even C (segv is
an exception mechanism...)
The question is really how to communicate failure to the caller, in a
way the caller can not ignore, without unduely inconvienencing the
caller.
So there is a two pronged test. Could the caller have anticipated the
failure, and should the caller anticipate failure.
head of an empty list is a good example of the first prong. It is easy
to check for. In many cases it can be shown to be impossible. So
burdoning all callers with dealing with failure explicitly is not
good. An exception is good here.
Contrast that with lookup in a table. The caller has no reasonable way
of telling before hand that the call will be succesful. The caller
should be prepared to handle lookup failure, so a Maybe is good here.
In C++ a null value of some kind is the norm.
One of the questions that C++ and Java answered was whether or not a
caller should be forced to deal with an exception explicitly by the
called function. The answer has turned out to be no, for somewhat
different reasons in both languages. But it is something that Haskell
should consider.
Particularly Java library integrator's discovery that strongly typed
exceptions do not scale. Frameworks like Tomcat seem to have found
that there isn't much middle ground between exceptions that are
handled close to the library, and just barely escape the library
interface, and the generic 'something went wrong somewhere',
'abort,retry,fail?' kind of error.
Of course, for library reuse, this is secondary to the incipient
package nameing disaster. Not tertiary, since it is at the heart of
lib composability....
On 3/12/07, Dougal Stanton
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?
I'm wondering both in terms of good engineering practise and also for correctness. Should elementary partial functions like
5 `div` 0
or
head []
return Nothing? I guess it's a bit of a silly suggestion, but it helps to highlight why we use Maybe in the first place. So --- where's the cutoff point in your code?
Cheers,
D. -- Dougal Stanton _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
participants (5)
-
Albert Y. C. Lai
-
Dougal Stanton
-
Gen Zhang
-
Neil Mitchell
-
Steve Downey