haskellfunctional-programmingmonadsmonoidsassociativity

How do operator associativity, the associative law and value dependencies of monads fit together?


On the one hand the monadic bind operator >>= is left associative (AFAIK). On the other hand the monad law demands associativity, i.e. evaluation order doesn't matter (like with monoids). Besides, monads encode a value dependency by making the next effect depend on the result of the previous one, i.e. monads effectively determine an evaluation order. This sounds contradictory to me, which clearly implies that my mental representation of the involved concepts is wrong. How does it all fit together?


Solution

  • On the one hand the monadic bind operator >>= is left associative

    Yes.

    Prelude> :i >>=
    class Applicative m => Monad (m :: * -> *) where
      (>>=) :: m a -> (a -> m b) -> m b
      ...
        -- Defined in ‘GHC.Base’
    infixl 1 >>=
    

    That's just the way it's defined. + is left-associative too, although the (addition-) group laws demand associativity.

    Prelude> :i +
    class Num a where
      (+) :: a -> a -> a
      ...
        -- Defined in ‘GHC.Num’
    infixl 6 +
    

    All an infixl declaration means is that the compiler will parse a+b+c as (a+b)+c; whether or not that happens to be equal to a+(b+c) is another matter.

    the monad law demands associativity

    Well, >>= is actually not associative. The associative operator is >=>. For >>=, already the type shows that it can't be associative, because the second argument should be a function, the first not.

    Besides, monads encode a value dependency by making the next effect depend on the result of the previous one

    Yes, but this doesn't contradict associativity of >=>. Example:

    teeAndInc :: String -> Int -> IO Int
    teeAndInc name val = do
       putStrLn $ name ++ "=" ++ show val
       return $ val + 1
    
    Prelude Control.Monad> ((teeAndInc "a" >=> teeAndInc "b") >=> teeAndInc "c") 37
    a=37
    b=38
    c=39
    40
    Prelude Control.Monad> (teeAndInc "a" >=> (teeAndInc "b" >=> teeAndInc "c")) 37
    a=37
    b=38
    c=39
    40
    

    Flipping around the parens does not change the order / dependency between the actions (that would be a commutativity law, not an associativity one), it just changes the grouping of the actions.