I'd like to make the nested applicative functors of different types. For example, nested simple functors of different types (in ghci) work fine:
Prelude> ((+2) <$>) <$> (Just [1..4])
Just [3,4,5,6]
But for applicative functors of different types:
Prelude> ((*) <$>) <$> (Just [1,2,3]) <*> (Just [4,5,6,7])
<interactive>:56:1: error:
* Couldn't match type `[Integer -> Integer]' with `[Integer] -> b'
isn't working! I want to obtain something like this:
Just [4,5,6,7,8,10,12,14,12,15,18,21]
I know that applicative functors have intermediate position between functors and monads. And I can see this exercise as preliminary before topic about monad transformers.
We may also do this quite stragithforward with prelude functions. Your first part is nice though.
((*) <$>) <$> (Just [1,2,3])
with typeNum a => Maybe [a -> a]
All we need is to fmap the applicative list in Maybe monad to a list in Maybe monad. So one approach could be to bind the first part to
(<$> Just [4, 5, 6, 7]) . (<*>) :: Num a => [a -> b] -> Maybe [b]
yields to
yields to
yields to
Besides nesting lifts and fmaps, another option to compose applicative functors is the
Data.Functor.Compose
newtype:for example:
Applicative
s are so well-behaved that a single newtype suffices to compose any two types that are instances. And there are other ways to combine them besides nesting, likeProduct
andDay
convolution:Monad
s do not compose as well though, so we need a different newtype for each monad in order to augment some other monad with the first monad's abilities. We call those newtypes monad transformers.In this case, you want:
Or:
The outer
… <$> … <*> …
orliftA2
operates onMaybe
, while the inner one operates on[]
. If you didn’t know this, you could figure it out by asking GHCi for the type of what you should put there, for example with a typed hole:It gives back:
And the behaviour you want for combining the lists is
\ xs ys -> (*) <$> xs <*> ys
, which can be abbreviatedliftA2 (*)
.((*) <$>)
orfmap (*)
didn’t work because that’s only half of what you need: it operates on a single list (usingFunctor
), while you want to combine two (usingApplicative
).Of course,
liftA2 (liftA2 (*))
works on any two nested applicative functors whose elements are numeric:For example, nested lists:
Or lists of
Maybe
:Or even something more exotic, like lists of functions: