Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

difference between isNothing and (== Nothing) in Haskell?

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)

like image 470
thor Avatar asked Apr 22 '18 06:04

thor


People also ask

What does nothing mean in Haskell?

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.

Can a Haskell function return nothing?

No. However, you can have functions that return a trivial value.

What does apostrophe mean in Haskell?

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.


3 Answers

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

like image 96
2 revs Avatar answered Oct 16 '22 17:10

2 revs


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.

like image 26
melpomene Avatar answered Oct 16 '22 18:10

melpomene


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
like image 4
Willem Van Onsem Avatar answered Oct 16 '22 19:10

Willem Van Onsem