Reputation: 233
I'm fooling with an example from Learn You a Haskell and I'm not sure what's going wrong. Here's the original example, which mimics truthy/falsey semantics:
class YesNo a where
yesno :: a -> Bool
A straightforward instance is given by:
instance YesNo Int where
yesno 0 = False
yesno _ = True
And then later:
instance YesNo (Maybe a) where
yesno (Just _) = True
yesno Nothing = False
This makes a certain amount of sense, but I find the notion that yesno (Just False) == True
to be a little counterintuitive, and so I tried to modify it like so:
instance YesNo (Maybe a) where
yesno (Just b) = yesno b
yesno Nothing = False
So that in the case where the Maybe instance contains a value we get the truthiness of that value itself. However, this fails with the error No instance for (YesNo a) arising from a use of
yesno'`. What am I doing wrong?
Upvotes: 3
Views: 178
Reputation: 120741
So that in the case where the Maybe instance contains a value we get...
That can't be done really well in Haskell. Type class instances shouldn't be read "I now transfer this data type from the set of non-instances to the set of instances", but rather "I now describe how to use this type as a instance of that class". There's not really a notion of not being an instance of a class, only of not finding an instance.
So if you want to decide based on the value contained in the Maybe
, you probably should do as suggested by Cirdec and Mikhail Glushenkov: instance YesNo a => YesNo (Maybe a)
. Of course that means e.g. Maybe ()
will not be a YesNo
instance. You either get the same behaviour for all types, or another, but again all-the-same behaviour for a particular set of types. But you can't get different behaviour depending on if a type is in the class.
There is actually one way to achieve that, but it's kind of frowned upon:
{-# LANGUAGE OverlappingInstances #-}
instance YesNo (Maybe a) where
yesno (Just _) = True
yesno Nothing = False
newtype YesNo_ a = YesNo_ a
instance YesNo a => YesNo (Maybe (YesNo_ a)) where
yesno (Just (YesNo_ a)) = yesno a
yesno Nothing = False
Not only is this cumbersome, it also requires the somewhat unsafe OverlappingInstances
extension.
Upvotes: 0
Reputation: 24166
You can make an instance of YesNo
for Maybe a
that only works when a
is also an instance of YesNo
:
instance (YesNo a) => YesNo (Maybe a) where
yesno (Just b) = yesno b
yesno Nothing = False
The (YesNo a) =>
says, "for every type a
, such that a
is an instance of YesNo
". The entire instance declaration the reads something like, "For every type a
, such that a
is an instance of YesNo
, Maybe a
is also an instance of YesNo
where ...
Upvotes: 0
Reputation: 15078
You need to tell the compiler that the type a
must have a YesNo
instance:
instance YesNo a => YesNo (Maybe a) where
yesno (Just a) = yesno a
yesno Nothing = False
Testing:
> yesno (Just False)
False
Upvotes: 3