Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is `Eq a` constraint required for the solution using `elem`?

Tags:

haskell

I was solving the following exercise from the Haskell Book:

-- >>> flipMaybe [Just 1, Just 2, Just 3]
-- Just [1, 2, 3]
-- >>> flipMaybe [Just 1, Nothing, Just 3]
-- Nothing

flipMaybe :: [Maybe a] -> Maybe [a]
flipMaybe = undefined

First I tried using elem,

flipMaybeWithElem :: [Maybe a] -> Maybe [a]
flipMaybeWithElem ms
  | Nothing `elem` ms = Nothing
  | otherwise         = Just (catMaybes ms)

but I got the error message:

misc.hs:86:5: error:
    • No instance for (Eq a) arising from a use of ‘elem’
      Possible fix:
        add (Eq a) to the context of
          the type signature for:
            flipMaybe2 :: forall a. [Maybe a] -> Maybe [a]
    • In the expression: Nothing `elem` ms
      In a stmt of a pattern guard for
                     an equation for ‘flipMaybe2’:
        Nothing `elem` ms
      In an equation for ‘flipMaybe2’:
          flipMaybe2 ms
            | Nothing `elem` ms = Nothing
            | otherwise = Just (catMaybes ms)
   |
86 |   | Nothing `elem` ms = Nothing
   |     ^^^^^^^^^^^^^^^^^

I understand that I should just add the Eq a => constraint to the function signature, but I was trying to stay true to the provided function stub. So I reused previous functions, and it did work:

flipMaybe :: [Maybe a] -> Maybe [a]
flipMaybe ms =
  case (filter isNothing ms) of
    [] -> Just (catMaybes ms)
    _  -> Nothing

The helper functions used:

isNothing :: Maybe a -> Bool
isNothing Nothing = True
isNothing _       = False

mayybee :: b -> (a -> b) -> Maybe a -> b
mayybee b _ Nothing = b
mayybee b f (Just a) = f a

fromMaybe :: a -> Maybe a -> a
fromMaybe a maybe = mayybee a id maybe

catMaybes :: [Maybe a] -> [a]
catMaybes xs = map (fromMaybe undefined) (filter isJust xs)

So why doesn't the first solution with elem work without the type constraints?

Is it simply because filter and isNothing have no constraints on the type variables and elem has? (Also with isNothing the type variable never even comes into play because it is ignored.)

> :t filter               
filter :: (a -> Bool) -> [a] -> [a]

> :t isNothing                              
isNothing :: Maybe a -> Bool                               

> :t elem
elem :: (Eq a, Foldable t) => a -> t a -> Bool 

Maybe has an Eq instance, but I guess the compiler does not know anything about a, right?

like image 896
toraritte Avatar asked Mar 27 '18 18:03

toraritte


Video Answer


2 Answers

Is it simply because filter and isNothing have no constraints on the type variables and elem has? (Also with isNothing the type variable never even comes into play because it is ignored.)

You've hit the nail on the head here. elem, in general, will only work if Eq a is satisfied. You're trying to use it on [Maybe a], and Maybe a has the following Eq instance.

instance Eq a => Eq (Maybe a) where
    ...

So elem looks at your Maybe a and says "I need this to be Eq". It sees the above instance and says "for Maybe a to satisfy Eq, a must satisfy Eq, and I don't know Eq a". Now, in your particular case, you only compare against Nothing, so the Eq a instance is never actually used. But the compiler doesn't have enough information to know this; it just knows that elem needs Eq a, and you don't provide it with that constraint.

In order to get this function to work without Eq, you need to do it the way you already did it, either with explicit recursion or with filter and isNothing. In short, I think you've already figured out the answer, and I'm just reiterating the things you've already said.

like image 103
Silvio Mayolo Avatar answered Oct 12 '22 01:10

Silvio Mayolo


Is it simply because filter and isNothing have no constraints on the type variables and elem has? (Also with isNothing the type variable never even comes into play because it is ignored.)

Correct, you nailed it here.

Maybe has an Eq instance, but I guess the compiler does not know anything about a, right?

Nailed it again.

like image 35
Daniel Wagner Avatar answered Oct 12 '22 02:10

Daniel Wagner