You can only make it an instance of Monad
if you wrap it in a newtype
. You also have to use the PolymorphicComponents
extension (a weaker form of RankNTypes
) to universally quantify the b
:
{-# LANGUAGE PolymorphicComponents #-}
newtype Maybe' a = Maybe' { unMaybe' :: forall b. (b -> (a -> b) -> b) }
just :: a -> Maybe' a
just a = Maybe' (\d f -> f a)
nothing :: Maybe' a
nothing = Maybe' const
bind :: Maybe' a -> (a -> Maybe' b) -> Maybe' b
bind ma f = Maybe' (unMaybe' ma const (\a -> unMaybe' (f a)))
instance Monad Maybe' where
return = just
(>>=) = bind
The reason you need a newtype is that Haskell type synonyms do not "stick". When Haskell tries to match the type signature of Maybe'
without the newtype against the Monad
type class, it does not see the Maybe'
at all and instead sees the raw underlying function type.
Haskell uses "principal types" to ensure that every type has a normal form. The normal form of the underlying function is:
(->) b ((->) ((->) a b) b)
Type synonyms do not change the normal form of a type, but newtypes do. Specifically, the newtype
in this case is rearranging the type so that the normal form now has the a
as the very last type parameter like the Monad
instance requires.