Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell - Is effect order deterministic in case of Applicative?

When executing the IO action defined by someFun <$> (a :: IO ()) <$> (b :: IO ()), is the execution of the a and b actions ordered? That is, can I count on that a is executed before b is?

For GHC, I can see the IO is implemented using State, and also see here that it is an Applicative instance, but can't find the source of the actual instance declaration. Being implemented through State suggests that different IO effects need to be sequential, but doesn't necessary defines their ordering.

Playing around in GHCi seems that Appliative retains effect order, but is that some universal guarantee, or GHC specific? I would be interested in details.

import System.Time
import Control.Concurrent
import Data.Traversable
let prec (TOD a b) = b
fmap (map prec) (sequenceA $ replicate 5 (threadDelay 1000 >> getClockTime))

[641934000000,642934000000,643934000000,644934000000,645934000000]

Thanks!

like image 715
ron Avatar asked Jan 10 '13 13:01

ron


3 Answers

It's certainly deterministic, yes. It will always do the same thing for any specific instance. However, there's no inherent reason to choose left-to-right over right-to-left for the order of effects.

However, from the documentation for Applicative:

If f is also a Monad, it should satisfy pure = return and (<*>) = ap (which implies that pure and <*> satisfy the applicative functor laws).

The definition of ap is this, from Control.Monad:

ap :: (Monad m) => m (a -> b) -> m a -> m b
ap =  liftM2 id

And liftM2 is defined in the obvious way:

liftM2 f m1 m2 = do { x1 <- m1; x2 <- m2; return (f x1 x2) }

What this means is that, for any functor that is a Monad as well as an Applicative, it is expected (by specification, since this can't be enforced in the code), that Applicative will work left-to-right, so that the do block in liftM2 does the same thing as liftA2 f x y = f <$> x <*> y.

Because of the above, even for Applicative instances without a corresponding Monad, by convention the effects are usually ordered left-to-right as well.

More broadly, because the structure of an Applicative computation is necessarily independent of the "effects", you can usually analyze the meaning of a program independently of how Applicative effects are sequenced. For example, if the instance for [] were changed to sequence right-to-left, any code using it would give the same results, just with the list elements in a different order.

like image 77
C. A. McCann Avatar answered Oct 16 '22 14:10

C. A. McCann


Yes, the order is predefined by the Monad-Applicative correspondence. This is easy to see: The (*>) combinator needs to correspond to the (>>) combinator in a well-behaved Applicative instance for a monad, and its definition is:

a *> b = liftA2 (const id) a b

In other words, if b were executed before a, the Applicative instance would be ill-behaving.

Edit: As a side note: This is not explicitly specified anywhere, but you can find many other similar correspondences like liftM2 = liftA2, etc.

like image 22
ertes Avatar answered Oct 16 '22 14:10

ertes


For the IO Applicative, this is certainly the case. But check out the async package for an example of an Applicative where in f <$> a <*> b the effects of a and b happen in parallel.

like image 2
Ben Millwood Avatar answered Oct 16 '22 15:10

Ben Millwood