Question

The Elm Guide says to use Maybe for partial functions, but I was under the impression that returning Maybe solves the problem of partial functions and makes them total. It gives a value from the codomain for every value in the domain.

Is a function returning Maybe (for example, converting a string to maybe int), partial or total?

Was it helpful?

Solution

You're absolutely right that using Maybe "solves the problem of partial functions and makes them total".

With that said, I think the advice "Use Maybe for Partial functions" is still both meaningful and insightful.

Lets take a typical partial function from Mathematics, the inverse cos function cos-1 or acos x, which is defined for the interval -1 < x < 1.

acos exists as a (partial) function in the Haskell standard library, with the signature

acos :: Floating a => a -> a

We can write a total version of acos by returning Nothing for values over which cos-1 isn't defined like so:

totalAcos :: (Ord a, Floating a) => a -> Maybe a
totalAcos x = do
    guard $ abs x <= 1 
    return $ acos x

This function is "total", in the sense that it's defined over the entire domain. However, that doesn't change the fact that the underlying mathematical function that we're modeling is a partial function in the mathematical sense, we're just able to represent this in a safe way, using Maybe.

Similarly, the example partial function in the Elm guide, String.toFloat, has the type signature String -> Maybe Float, can be seen as representing a mapping between String and Float, that is only defined for some Strings.

Modeling partial functions like this comes with a whole bunch of advantages, for instance the compiler is able to check that you're handling both cases, when your argument is inside or outside the domain, hence the advice in the Elm guide.

In short, Functions returning Maybe are generally total in the strict sense, but can be used to safely model partial functions.

OTHER TIPS

This is just some ambiguous terminology.

If you have a function that returns, say, Maybe String, one way to think of it is as a total function that returns Maybe String, but another way to think of it is as a partial function that returns String. It just depends on the context, on what would be more useful at the moment.

Of course, there are also "for realz" partial functions - these don't always return a value, but aren't indicating it in the type signature. In Haskell you can produce such functions via incomplete pattern match, via intrinsic errors, such as division by zero, by calling error explicitly, or with an infinite loop (aka "diverging computation").

The last possiblity - infinite loop - is more important than it looks at first. See, due to the Halting Problem, it is actually impossible for the compiler to tell, just by looking at the function, if it will always converge. And this means that any function can be potentially partial in the "for realz" sense, regardless of what its type signature says and how strict you can make the compiler.

In Haskell this issue is sort-of side-stepped by declaring that every Haskell type always includes a special value called "bottom" and denoted , which represents the result of a diverging computation. Of course if doesn't really help in practical programming, but on the other hand, in most other languages this issue is not even discussed at all, so this is a step forward.

Licensed under: CC-BY-SA with attribution
scroll top