Reputation: 7394
Over at the Edx Haskell course, Erik Meijer repeatedly states that using the Maybe
type for failed computations is not something one should do; instead, one should use the empty list for that.
My understanding is that the Maybe
type is a good thing and that we should use it. However, it seems that a list can model everything a Maybe
can model and more... So why do we need the Maybe
type at all?
Upvotes: 7
Views: 874
Reputation: 30237
I think I would join the choir here saying that I can't evaluate Meijer's recommendation unless I was presented with all of the details of his argument for it. To me it seems simple:
Maybe
for functions that return 0 or 1 results.[]
for functions that return 0 or more results.listToMaybe :: [a] -> Maybe a
and maybeToList :: Maybe a -> [a]
from Data.Maybe
to adapt functions written in one style to work in the other.Maybe
or []
, you can use the Alternative
or MonadPlus
classes.Example of point #4:
import Control.Applicative (pure, Alternative(..))
safeDiv :: (Alternative f, Fractional a, Eq a) => a -> a -> f a
safeDiv _ 0 = empty
safeDiv x y = pure (x / y)
{-
>>> safeDiv 5 2 :: Maybe Float
Just 2.5
>>> safeDiv 5 0 :: Maybe Float
Nothing
>>> safeDiv 5 2 :: [Float]
[2.5]
>>> safeDiv 5 0 :: [Float]
[]
-}
bothSqrt :: (Alternative f, Floating a) => a -> f a
bothSqrt x = let x' = sqrt x
in pure x' <|> pure (-x')
{-
>>> bothSqrt 5 :: Maybe Float
Just 2.236068
>>> bothSqrt 5 :: [Float]
[2.236068,-2.236068]
>>> bothSqrt 5 >>= flip safeDiv 2 :: Maybe Float
Just 1.118034
>>>> bothSqrt 5 >>= flip safeDiv 2 :: [Float]
[1.118034,-1.118034]
-}
Upvotes: 1
Reputation: 27766
In favor of lists:
The extra values are not a problem. The client can always choose to ignore the rest of the list when there is more than one result.
Using only lists avoids tedious conversions between Maybe and lists when we have to mix both. No need for listToMaybe
or maybeToList
.
catMaybes
becomes just concat
(or join
).
One possible concern is that repeated uses of (>>=)
for the list monad can create really big lists. However, Haskell is lazy. If we only ever use the first element, the rest of the list won't be computed.
>>> head (let xs = [1..1000000] in xs >>= \_ -> xs >>= \_ -> xs)
1
Upvotes: 1
Reputation: 1918
Another valuable point of Maybe is that it is just the simplest case of error-handling monads, which can be used to represent and compose „breakable“ computations in a convenient and consistent way (another examples are Either and pure exceptions). The list monad is semantically different (it represents non-deterministic computations) and has a similar behaviour only in the empty/singleton case, as it was shown above.
Upvotes: 0
Reputation: 105905
A list can model an arbitrary number of results. On the other hand, Maybe
models exactly one result or no result at all.
Think about the following functions:
f1 :: A -> [B]
f2 :: B -> [C]
f3 :: C -> [D]
f4 :: D -> [E]
It's not clear how many elements f1
, f2
, f3
or f4
return. So what happens if you sequence them?
f :: A -> [E]
f s = f1 s >>= f2 >>= f3 >>= f4
How many elements should the result contain? One? Zero? Did we accidentally create a list with n^n (n ~ input length) elements?
However, if the computations are going to return exactly one value or no value at all, the proper type gives us all the necessary information right away:
f1 :: A -> Maybe B
f2 :: B -> Maybe C
f3 :: C -> Maybe D
f4 :: D -> Maybe E
f :: A -> Maybe E
f s = f1 s >>= f2 >>= f3 >>= f4
So that's that. Now back to Meijer's statement:
Erik Meijer repeatedly states that using the Maybe type for failed computations is not something one should do; instead, one should use the empty list for that.
Without any additional objective reasoning, this is just a personal preference. I could tell everyone that fmap
is better than map
, it's something we should do. And at that point, you either believe me, or you ask questions. If he didn't make it clear in his lecture, ask him directly.
Upvotes: 8
Reputation: 29110
However it seems that a list can model everything a Maybe can model and more
The "and more" is an excellent reason to use Maybe
. As a consumer of a list, you need to be able to handle zero, one or multiple values. As a consumer of a Maybe
, you only need to be able to handle zero or one values. So in cases where multiple values don't make sense, it is much better to use Maybe
so that you know statically that you won't get nonsensical values.
Upvotes: 17