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.
Applicative functors are the programming equivalent of lax monoidal functors with tensorial strength in category theory. Applicative functors were introduced in 2008 by Conor McBride and Ross Paterson in their paper Applicative programming with effects.
sequence turns a list of optional values into an optional list. This means that in the first case of sequence , the return [] applies to Maybe 's return and means Just [] .
Functor is also important in its role as a superclass of Applicative and of Traversable . When working with these more powerful abstractions, it's often very useful to reach for the fmap method. Show activity on this post. For example, it's possible to derive the function lift in a way that works for any functor.
Monads are not a replacement for applicative functors Instead, every monad is an applicative functor (as well as a functor).
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)]
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With