Reputation: 261
I'm learning Haskell with the Learn You a Haskell guide, and I'm stuck on an example of applicative sequencing over a list of functions. Chapter 11: Functors, Applicative Functors and Monoids defines sequenceA to be:
sequenceA :: (Applicative f) => [f a] -> f [a]
sequenceA [] = pure []
sequenceA (x:xs) = (:) <$> x <*> sequenceA xs
I'm a bit confounded by this example usage of sequenceA:
> sequenceA [(+3),(+2),(+1)] 3
[6,5,4]
I've expanded the application manually as far as I could to what I believe is correct:
(:) <$> (+3) <*> sequenceA [(+2), (+1)]
(:) <$> (+3) <*> (:) <$> (+2) <*> (:) <$> (+1) <*> sequenceA []
(:) <$> (+3) <*> (:) <$> (+2) <*> (:) <$> (+1) <*> pure []
(:) <$> (+3) <*> (:) <$> (+2) <*> (:) <$> (+1) <*> const []
What I don't see though, is how the second argument to the original application of sequenceA, 3
gets applied to each of the partially applied functions given in the list. I would appreciate some assistance in trying to wrap my head around what goes on in the evaluation of this statement.
Upvotes: 6
Views: 1578
Reputation: 74354
Whenever you're dealing with generic types you need to first determine what the concrete type is. In this case, we have
sequenceA :: Applicative f => [f a] -> f [a]
applied to
[(+3),(+2),(+1)] :: Num a => [a -> a]
In other words, though it's a bit bizarre to see at first, f
becomes a ->
(properly written (->) a
) and so the type of sequenceA
, specialized, is
sequenceA :: Num a => [a -> a] -> a -> [a]
Which should already explain where that extra argument came from.
So how does sequenceA
work? To understand, we need to understand the Applicative
instance for (->) a
. In particular
instance Functor ((->) a) where
fmap f g = f . g
instance Applicative ((->) a) where
pure a' = \a -> a'
ff <*> fx = \a -> (ff a) (fx a)
where we see that (<*>)
takes two functions and produces a third. The argument from this third function is applied to each of the input functions (here, ff
and fx
) and then their results are applied to one another.
So application in the (->) a
monad means that the ultimate argument a
to the result is distributed to all of the compositional functions.
And this is what we see with sequenceA
sequenceA [(+3),(+2),(+1)]
==
\a -> [(a + 3), (a + 2), (a + 1)]
Upvotes: 12
Reputation: 9402
Using:
instance Applicative ((->) a) where
pure = const
(<*>) f g x = f x (g x)
we can derive:
(:) <$> (+3) <*> ( (:) <$> (+2) <*> ( (:) <$> (+1) <*> const [] ))
pure (:) <*> (+3) <*> ( pure (:) <*> (+2) <*> ( pure (:) <*> (+1) <*> const [] ))
const (:) <*> (+3) <*> (const (:) <*> (+2) <*> (const (:) <*> (+1) <*> const [] ))
(const (:) <*> (+3)) <*> ((const (:) <*> (+2)) <*> ((const (:) <*> (+1)) <*> const [] ))
(const (:) <*> (+3)) <*> ((const (:) <*> (+2)) <*> ((\x -> (const (:)) x (x+1)) <*> const [] ))
(const (:) <*> (+3)) <*> ((const (:) <*> (+2)) <*> ((\x -> (:) (x+1)) <*> const [] ))
(const (:) <*> (+3)) <*> ((const (:) <*> (+2)) <*> (\y -> (\x -> (:) (x+1)) y (const [] y) ))
(const (:) <*> (+3)) <*> ((const (:) <*> (+2)) <*> (\y -> (\x -> (:) (x+1)) y [] ))
(const (:) <*> (+3)) <*> ((const (:) <*> (+2)) <*> (\y -> ((y+1):) [] ))
(const (:) <*> (+3)) <*> ((const (:) <*> (+2)) <*> (\y -> [y+1] ))
(const (:) <*> (+3)) <*> (((\x -> (const (:)) x (x+2)) <*> (\y -> [y+1] ))
(const (:) <*> (+3)) <*> (\z -> ((\x -> (const (:)) x (x+2)) z ((\y -> [y+1] )z))
(const (:) <*> (+3)) <*> (\z -> (((const (:)) z (z+2)) ([z+1]))
(const (:) <*> (+3)) <*> (\z -> ((z+2):) [z+1])
(const (:) <*> (+3)) <*> (\z -> [z+2,z+1])
(\x -> (const (:)) x (x+3)) <*> (\z -> [z+2,z+1])
(\x -> const (:) (x+3)) <*> (\z -> [z+2,z+1])
(\x -> ((x+3):)) <*> (\z -> [z+2,z+1])
\w -> (\x -> ((x+3):)) w ((\z -> [z+2,z+1]) w)
\w -> ((w+3):) [w+2,w+1]
\w -> [w+3,w+2,w+1]
I bet I messed up some parentheses
Upvotes: 2