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?
Is it simply because
filter
andisNothing
have no constraints on the type variables andelem
has? (Also withisNothing
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.
Is it simply because
filter
andisNothing
have no constraints on the type variables andelem
has? (Also withisNothing
the type variable never even comes into play because it is ignored.)
Correct, you nailed it here.
Maybe
has anEq
instance, but I guess the compiler does not know anything abouta
, right?
Nailed it again.
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