Exercise solutions
Exercise 1
Try implementing
liftA2
forMaybe
.
The type looks like this.
@Maybe :: (a -> b -> c) ->
liftA2 Maybe a -> Maybe b -> Maybe c
As we said, it’s very close to fmap
which you have implemented before. We’ll call the function argument f
, but you might have called it something else.
Here’s another flavor – semantically the same, syntactically different.
You could, of course, write a little property test to make sure that it’s behaving the same as liftA2 @Maybe
from base
, but that’s probably not necessary.
Exercise 2
Given the similarities with
Functor
, see if you can write aninstance
ofApplicative
forEither
. You can implement either(<*>)
orliftA2
or both. You may want to un-importEither
and its constructors fromPrelude
. You’ll need to have aFunctor
instance in scope forEither
.
Remember you have a choice of implementing the tie-fighter or liftA2
for your instance, but you must have pure
either way. We’ve used the Either
names for our instances here (un-importing them from Prelude
).
The types of the (<*>)
and liftA2
as specialized to Either a
are as follows:
λ> :type (<*>) @(Either _)
(<*>) @(Either _)
:: Either w (a -> b) -> Either w a -> Either w b
λ> :type liftA2 @(Either _)
liftA2 @(Either _)
:: (a -> b -> c) ->
Either w a -> Either w b -> Either w c
Left
values, which contain the w
, are treated the same as Nothing
in the Maybe
instances.
Alternatively:
You should be noticing a pattern at this point, not just with these but a commonality with the structure of Functor
instances, too. There is usually only one sensible implementation for a given typeFor Functor
there is only ever one sensible implementation, or so we’re told – we’ve never proven this ourselves. Therefore, there is a language extension you can use to derive Functor
instances, much as you derive Show
and others. For now, we’d encourage you to keep writing instances by hand as long as you can stand it. The day you find yourself thinking how trivial it is to write this next Functor
instance, it’s probably safe to start deriving them., and you just do the simplest thing. Part of the reason we have you write so many instances – and you should be writing even more! – is because doing these until they are mechanical because you’ve internalized the pattern is the best way we know to make these patterns really useful to you.