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 Maybes:
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 Nothings, and two Justs, where the values the Justs 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