Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does applicative sequencing over a list of functions work?

Tags:

haskell

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.

like image 392
bpaterni Avatar asked Jun 30 '14 22:06

bpaterni


People also ask

What is applicative functional programming?

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.

How does sequence work Haskell?

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

Why is functor useful?

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.

Are all monads applicative functors?

Monads are not a replacement for applicative functors Instead, every monad is an applicative functor (as well as a functor).


2 Answers

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)]
like image 129
J. Abrahamson Avatar answered Sep 18 '22 23:09

J. Abrahamson


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

like image 21
Karol S Avatar answered Sep 18 '22 23:09

Karol S