(This answer assumes you know what an Applicative
is; if you don’t, Learn You a Haskell may be a useful resource.)
Roughly speaking, sequenceA
is a method of taking several Applicative
values and running them. To start, let’s have a look at its type signature. You’ve defined a restricted version, but the full type signature is:
sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
In general, Haskell allows you to learn a lot about a function from its type signature alone, and this is no exception. To start, let’s have a look at its input and output: it takes as input a Traversable
containing Applicative
elements, and returns an Applicative
containing Traversible
elements.
Now, how can we get from t (f a)
to f (t a)
? This may be easier to understand if you specialise t
to []
, as that is an instance of Traversible
:
sequenceA :: (Applicative f) => [f a] -> f [a]
Now this starts to make more sense: given a list of f a
s, extract the a
s and return them as a list. The idea is that a value of type f x
, where f
is an Applicative
, is an effectful value: a value which could potentially have some side-effects, but then returns a value of type x
. So sequenceA
acts to accumulate effects: if you have a list of effectful values [f a]
, then sequenceA
takes each f a
in sequence, runs its effect, then returns all the a
s together. It returns these values as an effectful list, since to return [a]
it has to run the effects inside the original list.
Now let’s go back to the original definition:
sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
We can interpret this in the same way: given a set of effectful values inside a Traversible
data structure, sequenceA
returns an effectful value created by running all the effects inside the Traversible
in turn, then taking the return value thus generated and putting it back inside the Traversible
. The idea of ‘in turn’ is very important: in some sense, the Traversible
instance (and hence sequenceA
, as one of its methods) creates the idea of an order inside a data structure, since there aren’t really any other Haskell functions which iterate over a data structure in some particular order.
Now, we can finally understand your example! Let’s consider what sequenceA (replicate n getChar)
does:
- As you noted,
replicate n getChar
repeats a list of [getChar, getChar, ..., getChar]
n times.
From the above description:
sequenceA
returns an effectful value created by running all the effects inside the Traversible
in turn, then taking the return value thus generated and putting it back inside the Traversible
.
So here, we take the list of getChar
s, run each effectful value (which has the effect of getting a character from the user and returning it), then taking the return value (i.e. the user’s character) and putting it back inside the Traversible
(i.e. putting the return values back in a list).
So sequenceA (replicate n getChar)
just gets n characters from the user, then returns them in a list.
It’s worth noting that this is far from the only use for sequenceA
. A small sample:
- The
Applicative
instance for lists takes each pair in turn from the give lists. For instance, (,) <$> [1,2] <*> ['a','b']
returns [(1,'a'),(1,'b'),(2,'a'),(2,'b')]
. So sequenceA [list1, list2, ...]
gets all combinations of elements i.e. the cartesian produce! For example, sequenceA [['a','b'], ['c','d'], ['e','f']]
‘runs’ the applicative by returning each combination, and then puts the results back in a list, giving ["ace","acf","ade","adf","bce","bcf","bde","bdf"]
.
- In general, any time you have a collection of effects you want to run, you should be reaching for
sequenceA
. (Or its close relative traverse
.) For example, recently I was writing a cellular automaton simulation program, and I had ended up with a data structure representing a grid with random elements. Since I was using the MonadRandom
package, my randomness was represented as an Applicative
, so I could use sequenceA
to run each random value in turn and turn a ‘grid of random values’ into a ‘random grid of values’, which was much easier to work with.