问题
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.
回答1:
In this case, you want:
liftA2 (*) <$> Just [1, 2, 3] <*> Just [4, 5, 6, 7]
Or:
liftA2 (liftA2 (*)) (Just [1, 2, 3]) (Just [4, 5, 6, 7])
The outer … <$> … <*> …
or liftA2
operates on Maybe
, 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:
:t _ <$> (Just [1 :: Int, 2, 3]) <*> (Just [4 :: Int, 5, 6, 7]) :: Maybe [Int]
It gives back:
_ :: [Int] -> [Int] -> [Int]
And the behaviour you want for combining the lists is \ xs ys -> (*) <$> xs <*> ys
, which can be abbreviated liftA2 (*)
. ((*) <$>)
or fmap (*)
didn’t work because that’s only half of what you need: it operates on a single list (using Functor
), while you want to combine two (using Applicative
).
Of course, liftA2 (liftA2 (*))
works on any two nested applicative functors whose elements are numeric:
(Applicative f, Applicative g, Num a)
=> f (g a) -> f (g a) -> f (g a)
For example, nested lists:
liftA2 (liftA2 (*)) [[1], [2], [3]] [[4, 5, 6]]
== [[4,5,6],[8,10,12],[12,15,18]]
-- (Transposing the inputs transposes the output.)
liftA2 (liftA2 (*)) [[1, 2, 3]] [[4], [5], [6]]
== [[4,8,12],[5,10,15],[6,12,18]]
Or lists of Maybe
:
liftA2 (liftA2 (*)) [Just 1, Nothing, Just 3] [Just 4, Nothing, Just 6]
== [Just 4, Nothing, Just 6,
Nothing, Nothing, Nothing,
Just 12, Nothing, Just 18]
Or even something more exotic, like lists of functions:
($ (3, 5)) <$> (liftA2 (+) <$> [fst, snd] <*> [snd, fst])
== [fst (3, 5) + snd (3, 5),
fst (3, 5) + fst (3, 5),
snd (3, 5) + snd (3, 5),
snd (3, 5) + fst (3, 5)]
== [3+5, 3+3, 5+5, 5+3]
== [8,6,10,8]
回答2:
Besides nesting lifts and fmaps, another option to compose applicative functors is the Data.Functor.Compose newtype:
newtype Compose f g a = Compose { getCompose :: f (g a) }
for example:
ghci> let Compose result = (*) <$> Compose (Just [1,2,3]) <*> Compose (Just [4,5,6,7])
ghci> result
Just [4,5,6,7,8,10,12,14,12,15,18,21]
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, like Product and Day convolution:
data Product f g a = Pair (f a) (g a)
data Day f g a = forall b c. Day (f b) (g c) (b -> c -> a)
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.
回答3:
We may also do this quite stragithforward with prelude functions. Your first part is nice though.
((*) <$>) <$> (Just [1,2,3])
with type Num 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]
((*) <$>) <$> (Just [1,2,3]) >>= (<$> Just [4,5,6,7]) . (<*>)
yields to
Just [(1*),(2*),(3*)] >>= (<$> Just [4,5,6,7]) . (<*>)
yields to
([(1*),(2*),(3*)] <*>) <$> Just [4,5,6,7]
yields to
Just [4,5,6,7,8,10,12,14,12,15,18,21]
来源:https://stackoverflow.com/questions/53680453/nested-applicative-functors-of-different-types-in-haskell