@Li-yao Xia's answer pretty much covers it, but if it helps your intuition, think of the Stream
monad as modeling an infinite sequence of parallel computations. A Stream
value itself is an (infinite) sequence of values, and I can use the Functor
instance to apply the same function in parallel to all values in the sequence; the Applicative
instance to apply a sequence of given functions to a sequence of values, pointwise with each function applied to the corresponding value; and the Monad
instance to apply a computation to each value in the sequence with a result that can depend on both the value and its position within the sequence.
As an example of some typical operations, here are some sample sequences plus a Show-instance
instance (Show a) => Show (Stream a) where
show = show . take 10 . toList
nat = go 1 where go x = Cons x (go (x+1))
odds = go 1 where go x = Cons x (go (x+2))
giving:
> odds
[1,3,5,7,9,11,13,15,17,19]
> -- apply same function to all values
> let evens = fmap (1+) odds
> evens
[2,4,6,8,10,12,14,16,18,20]
> -- pointwise application of functions to values
> (+) <$> odds <*> evens
[3,7,11,15,19,23,27,31,35,39]
> -- computation that depends on value and structure (position)
> odds >>= \val -> fmap (\pos -> (pos,val)) nat
[(1,1),(2,3),(3,5),(4,7),(5,9),(6,11),(7,13),(8,15),(9,17),(10,19)]
>
The difference between the Applicative
and Monad
ic computations here is similar to other monads: the applicative operations have a static structure, in the sense that each result in a <*> b
depends only on the values of the corresponding elements in a
and b
independent of how they fit in to the larger structure (i.e., their positions in the sequence); in contrast, the monadic operations can have a structure that depends on the underlying values, so that in the expression as >>= f
, for a given value a
in as
, the corresponding result can depend both on the specific value a
and structurally on its position within the sequence (since this will determine which element of the sequence f a
will provide the result).
It turns out that in this case the apparent additional generality of monadic computations doesn't translate into any actual additional generality, as you can see by the fact that the last example above is equivalent to the purely applicative operation:
(,) <$> nat <*> odds
More generally, given a monadic action f :: a -> Stream b
, it will always be possible to write it as:
f a = Cons (f1 a) (Cons (f2 a) ...))
for appropriately defined f1 :: a -> b
, f2 :: a -> b
, etc., after which we'll be able to express the monadic action as an application action:
as >>= f = (Cons f1 (Cons f2 ...)) <*> as
Contrast this with what happens in the List
monad: Given f :: a -> List b
, if we could write:
f a = [f1 a, f2 a, ..., fn a]
(meaning in particular that the number of elements in the result would be determined by f
alone, regardless of the value of a
), then we'd have the same situation:
as >>= f = as <**> [f1,...,fn]
and every monadic list operation would be a fundamentally applicative operation.
So, the fact that not all finite lists are the same length makes the List
monad more powerful than its applicative, but because all (infinite) sequences are the same length, the Stream
monad adds nothing over the applicative instance.