I'm confused about why the two functions below involving Nothing
are different:
coalesce m1 m2 = if isNothing m1 then m2 else m1
coalesce' m1 m2 = if (m1 == Nothing) then m2 else m1
The first one has type:
λ> :t coalesce
coalesce :: Maybe a -> Maybe a -> Maybe a
as expected. But the second one has:
λ> :t coalesce'
coalesce' :: Eq a => Maybe a -> Maybe a -> Maybe a
Why does using (==Nothing)
introduce the Eq a
constraint?
(GHC 8.2.2)
Nothing is a value, and its concrete type may be any possible instantiation of Maybe a . Otherwise said, values of type Maybe a continue to have concrete types Maybe Int , Maybe String , Maybe Whatever , and in particular, Nothing is able to be typed by each of them, depending on the context.
No. However, you can have functions that return a trivial value.
In Haskell it is just another character to distinguish identifiers and the identifier is then called fold prime , but it is commonly used in the same way as it used in mathematics.
==
is a function Eq a => a -> a -> Bool
. You’re making one of its operands Nothing
, so a
is a Maybe b
for some type b
, but that Eq a
restriction still applies – the Maybe b
has to have an Eq
instance. Maybe b
includes a definition for such an instance – Eq (Maybe b)
– for all Eq b
.
In other words, ==
is just a function and it doesn’t know ahead of time that you’re providing Nothing
as an argument. It only knows it’s some Maybe a
, and it needs to be able to compare equality if it happens to be a Just …
.
In other other words, here’s how you can define the ==
that already exists for Maybe
s:
equals a b = case a of
Nothing -> isNothing b
Just x -> case b of
Nothing -> False
Just y -> x == y
Notice how x == y
appears, meaning there must be an Eq
instance for the type of x
and y
.
Because the type of ==
is
(==) :: (Eq a) => a -> a -> Bool
Your second definition (coalesce'
) uses ==
, so it inherits the Eq
constraint on its arguments from ==
.
Strictly speaking coalesce'
uses ==
on values of type Maybe a
, but there's an instance
instance (Eq a) => Eq (Maybe a) where ...
so the Eq (Maybe a)
constraint becomes Eq a
, because that's what's needed to support ==
on Maybe a
.
Let us first aim to design a (==)
function over Maybe
values. Usually we consider two things the same if they have the same data constructor, and the parameters are all equal. So we consider F x1 x2 x3
and G y1 y2 y3
the same if F
and G
are the same data constructor, and x1 == y1
, x2 == y2
, and x3 == y3
.
So if we implement this for Maybe
, there are two cases that are equal: two Nothing
s, and two Just
s, where the values the Just
s encapsulate are the same, so:
instance Eq a => Eq (Maybe a) where
(==) Nothing Nothing = True
(==) (Just x) (Just y) = x == y
(==) _ _ = False
is the most logical way to implement the Eq
instance for Maybe a
. In the implementation, we use x == y
, hence we added the Eq a
type constraint.
Now Haskell sees functions conceptually as blackboxes. For Maybe
, it thus sees (==)
as (==) :: Eq a => Maybe a -> Maybe a -> Bool
. If you thus use this (==)
function, it will always require the type constraint Eq a
, regardless whether the specific usage will ever need this type constraint to perform the x == y
check. If you write (== Nothing)
, then we see, that the second clause ((==) (Just x) (Just y)
) will never be used, but since Haskell treats functions as blackboxes, it does not known in what cases the type constraint is relevant.
isNothing :: Maybe a -> Bool
only checks if the value is a Nothing
, if it is a Just
, then it is always False
, regardless what the value is the Just
constructor wraps, it is thus implemented like:
isNothing :: Maybe a -> Bool
isNothing Nothing = True
isNothing _ = False
So we do not need the Eq a
type constraint here: we do not check equality of elements Just
constructor(s) wrap. So again if we use this, Haskell only checks the type signature, notices that there is no Eq a
type constraint involved and hence does not add it to functions that use this function.
Notice that what you implement here actually already is implemented in the Control.Applicative
module: (<|>) :: Alternative f => f a -> f a -> f a
, for example:
Prelude> import Control.Applicative
Prelude Control.Applicative> (<|>) Nothing Nothing
Nothing
Prelude Control.Applicative> (<|>) Nothing (Just 3)
Just 3
Prelude Control.Applicative> (<|>) (Just 3) Nothing
Just 3
Prelude Control.Applicative> (<|>) (Just 3) (Just 2)
Just 3
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With