1) how to define a monad structure for my 'User a' type
(Preliminary note: Learn You a Haskell predates Applicative
becoming a superclass of Monad
. For that reason, and unlike the book suggests, with recent versions of GHC there is no need of implementing return
-- by default, it is the same as pure
. That being so, I will jump directly to (>>=)
.)
One nice thing about methods with very general types such as (>>=)
...
(>>=) :: Monad m => m a -> (a -> m b) -> m b
... is that they are so general that there are few sensible ways of implementing them -- often just one. If we specialise (>>=)
for your type, we get:
(>>=) @User :: User a -> (a -> User b) -> User b
So (>>=)
is supposed to take an User a
and an a -> User b
and use them to produce an User b
. Given that the function takes an a
value, the first thing to try is looking for an a
value to pass to it. We do have such a value: the one wrapped by the Age
constructor in the User a
value:
instance Monad User where
(Age a) >>= f = f a
To write a Monad
instance in good conscience, we should check whether it follows the monad laws:
return a >>= f = f a
m >>= return = m
(m >>= f) >>= g = m >>= (\x -> f x >>= g)
That can be done with (your favourite equivalent of) pen and paper:
return a >>= f = f a
Age a >>= f = f a
f a = f a
m >>= return = m
Age a >>= return = Age a -- [*]
Age a >>= Age = Age a
Age a = Age a
-- [*]: Both here and in the next law, we can replace 'm' by 'Age a'
-- because there is just one constructor. If there were more
-- (as in e.g. data Maybe a = Nothing | Just a), we would have
-- to check the law holds for all constructors.
(m >>= f) >>= g = m >>= (\x -> f x >>= g)
(Age a >>= f) >>= g = Age a >>= (\x -> f x >>= g)
f a >>= g = (\x -> f x >>= g) a
f a >>= g = f a >>= g
2) what functionality a monad provides versus, say, an applicative functor?
Here I will leave it to the question arrowd suggested. I will only note that your example doesn't give you enough to really appreciate the difference, as the User
context is too simple to let you do anything interesting with (>>=)
(as Benjamin Hodgson points out, it is isomorphic to Identity
, the dummy functor). Maybe
, which is used by the accepted answer of the linked question, is quite a bit more illustrative.
User
type is isomorphic to theIdentity
functor. Have a look at its source code – Benjamin Hodgson♦