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