4

Consider the following Haskell statement:

mapM print ["1", "2", "3"]

Indeed, this prints "1", "2", and "3" in order.

Question: How do you know that mapM will first print "1", and then print "2", and finally print "3". Is there any guarantee that it will do this? Or is it a coincidence of how it is implemented deep within GHC?

duplode
  • 33,731
  • 7
  • 79
  • 150
George
  • 6,927
  • 4
  • 34
  • 67

3 Answers3

10

If you evaluate mapM print ["1", "2", "3"] by expanding the definition of mapM you will arrive at (ignoring some irrelevant details)

print "1" >> print "2" >> print "3"

You can think of print and >> as abstract constructors of IO actions that cannot be evaluated any further, just as a data constructor like Just cannot be evaluated any further.

The interpretation of print s is the action of printing s, and the interpretation of a >> b is the action that first performs a and then performs b. So, the interpretation of

mapM print ["1", "2", "3"] = print "1" >> print "2" >> print "3"

is to first print 1, then print 2, and finally print 3.

How this is actually implemented in GHC is entirely a different matter which you shouldn't worry about for a long time.

Reid Barton
  • 14,951
  • 3
  • 39
  • 49
  • I prefer to break it down as far as `print = putStrLn . show`. You could go a bit further, but that at least explains the role of `Show`. – dfeuer Nov 04 '16 at 18:40
7

There is no guarantee on the order of the evaluation but there is a guarantee on the order of the effects. For more information see this answer that discusses forM.

You need to learn to make the following, tricky distinction:

  1. The order of evaluation
  2. The order of effects (a.k.a. "actions")

What forM, sequence and similar functions promise is that the effects will be ordered from left to right. So for example, the following is guaranteed to print characters in the same order that they occur in the string...

Note: "forM is mapM with its arguments flipped. For a version that ignores the results see forM_."

Community
  • 1
  • 1
Dair
  • 15,910
  • 9
  • 62
  • 107
4

Preliminary note: The answers by Reid Barton and Dair are entirely correct and fully cover your practical concerns. I mention that because partway through this answer one might have the impression that it contradicts them, which is not the case, as will be clear by the time we get to the end. That being clear, it is time to indulge in some language lawyering.

Is there any guarantee that [mapM print] will [print the list elements in order]?

Yes, there is, as explained by the other answers. Here, I will discuss what might justify this guarantee.

In this day and age, mapM is, by default, merely traverse specialised to monads:

traverse
  :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
mapM
  :: (Traversable t, Monad m) => (a -> m b) -> t a -> m (t b)

That being so, in what follows I will be primarily concerned with traverse, and how our expectations about the sequencing of effects relate to the Traversable class.

As far as the production of effects is concerned, traverse generates an Applicative effect for each value in the traversed container and combines all such effects through the relevant Applicative instance. This second part is clearly reflected by the type of sequenceA, through which the applicative context is, so to say, factored out of the container:

sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)

-- sequenceA and traverse are interrelated by:
traverse f = sequenceA . fmap f
sequenceA = traverse id

The Traversable instance for lists, for example, is:

instance Traversable [] where
    {-# INLINE traverse #-} -- so that traverse can fuse
    traverse f = List.foldr cons_f (pure [])
      where cons_f x ys = (:) <$> f x <*> ys

It is plain to see that the combining, and therefore the sequencing, of effects is done through (<*>), so let's focus on it for a moment. Picking the IO applicative functor as an illustrative example, we can see (<*>) sequencing effects from left to right:

GHCi> -- Superfluous parentheses added for emphasis.
GHCi> ((putStrLn "Type something:" >> return reverse) <*> getLine) >>= putStrLn
Type something:
Whatever
revetahW

(<*>), however, sequences effects from left-to-right by convention, and not for any inherent reason. As witnessed by the Backwards wrapper from transformers, it is, in principle, always possible to implement (<*>) with right-to-left sequencing and still get a lawful Applicative instance. Without using the wrapper, it is also possible to take advantage of (<**>) from Control.Applicative to invert the sequencing:

(<**>) :: Applicative f => f a -> f (a -> b) -> f b
GHCi> import Control.Applicative
GHCi> (getLine <**> (putStrLn "Type something:" >> return reverse)) >>= putStrLn
Whatever
Type something:
revetahW

Given that it is so easy to flip the sequencing of Applicative effects, one might wonder whether this trick might transfer to Traversable. For instance, let's say we implement...

esrevart :: Applicative f => (a -> f b) -> [a] -> f [b]

... so that it is just like traverse for lists save for using Backwards or (<**>) to flip the sequencing of effects (I will leave that as an exercise for the reader). Would esrevart be a legal implementation of traverse? While we might figure it out by trying to prove the identity and composition laws of Traversable hold, that is actually not necessary: given that Backwards f for any applicative f is also applicative, an esrevart patterned after any lawful traverse will also follow the Traversable laws. The Reverse wrapper, also part of transformers, offers a general implementation of this reversal.

We have thus concluded that there can be legal Traversable instances that differ in the sequencing of effects. In particular, a list traverse that sequences effects from tail to head is conceivable. That doesn't make the possibility any less strange, though. To avoid utter bewilderment, Traversable instances are conventionally implemented with plain (<*>) and following the natural order in which the constructors are used to build the traversable container, which in the case of lists amounts to the expected head-to-tail sequencing of effects. One place where this convention shows up is in the automatic generation of instances by the DeriveTraversable extension.

A final, historical note. Couching this discussion, which is ultimately about mapM, in terms of the Traversable class would be a move of dubious relevance in a not so distant past. mapM was effectively subsumed by traverse only last year, but it has existed for much longer. For instance, the Haskell Report 1.3 from 1996, years before Applicative and Traversable came into being (not even ap is there, in fact), provides the following specification for mapM:

accumulate        :: Monad m => [m a] -> m [a]
accumulate        =  foldr mcons (return [])
                     where mcons p q = p >>= \x -> q >>= \y -> return (x:y)

mapM              :: Monad m => (a -> m b) -> [a] -> m [b]
mapM f as         =  accumulate (map f as)

The sequencing of effects, here enforced through (>>=), is left-to-right, for no other reason than it being the sensible thing to do.

P.S.: It is worth emphasising that, while it is possible to write a right-to-left mapM in terms of the Monad operations (in the Report 1.3 implementation quoted here, for instance, it merely requires exchanging p and q in the right-hand side of mcons), there is no such thing as a general Backwards for monads. Since f in x >>= f is a Monad m => a -> m b function which creates effects from values, the effects associated with f depend on x. As a consequence, a simple inversion of sequencing like that possible with (<*>) is not even guaranteed to be meaningful, let alone lawful.

Community
  • 1
  • 1
duplode
  • 33,731
  • 7
  • 79
  • 150
  • 1
    Implementing the `Traversable` instance for `Data.Functor.Reverse` is not such a trivial exercise, and relies essentially on `Backwards`. It may bear mention that there can be nothing like `Backwards` for the `Monad` class. – dfeuer Nov 04 '16 at 18:49
  • @dfeuer [1] Indeed. (What makes it a little trickier is that in the general `(Traversable f) => Traversable (Reverse f)` case you can't play with neither `(<*>)` nor the structure of the (unknown) `Traversable`.) By the way, I will add a link to `Data.Functor.Reverse`. [2] That is definitely worth mentioning; I will add a note about it. (I had chosen not to include an extra paragraph about `Monad`, and that remark was supposed to be made somewhere in it.) Useful reminders, thanks. – duplode Nov 04 '16 at 19:09