简体   繁体   中英

Nested applicative functors of different types in Haskell

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.

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.

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]

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 [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] 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM