Suppose i have a datatype MayFail
defined as following
data MayFail e a = Error e | Result a
deriving (Show)
So it's either a result or an error. I now want to write a Functor
for it but this is where it gets confusing.
MayFail
has two types, either e
or a
. So why do I have to write the functor as follows
instance Functor (MayFail e) where
fmap _ (Error e) = Error e
fmap f (Result x) = Result (f x)
and not instance Functor (MayFail e a) where
?
What is the syntactic rule behind this?
Your question is a bit unclear, but I assume you're asking why you have to use e
in instance Functor (MayFail e)
instead of just writing instance Functor MayFail
.
This is because Functor
takes a type parameter of kind Type -> Type
, and MayFail
on its own would have kind Type -> Type -> Type
. (Using MayFail e a
would also be wrong, as its kind is just Type
.)
MayFail :: Type -> Type -> Type
is not a functor, but a bifunctor:
-- somewhat simplified definition
class Bifunctor p where
-- p :: Type -> Type -> Type
bimap :: (a -> c) -> (c -> d) -> p a b -> p c d
instance Bifunctor MayFail where
bimap f _ (Error e) = Error (f e)
bimap _ g (Result x) = Result (g x)
But, for any fixed error type e
, the result of the partial application MayFail e :: Type -> Type
is a functor:
instance Functor (MayFail e) where
fmap _ (Error e) = Error e
fmap f (Result x) = Result (f x)
-- Or, using the Bifunctor instance,
-- fmap = bimap id
In some sense, a bifunctor is a mapping of types to functors.
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