Search code examples
haskellclosuresmonadsassociativity

unbound variables in monad associativity law


Using ghci I have computed:

Prelude> let m = [1,2]
Prelude> let ys = [4, 5, 6]
Prelude> m >>= (\x -> ys >>= (\y -> return (x, y)))
[(1,4),(1,5),(1,6),(2,4),(2,5),(2,6)]

The monadic expression above doesn't seem to correspond to either side of the monad associativity law:

(m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)

I would like to know how monad associativity can be applied to the expression:

m >>= (\x -> ys >>= (\y -> return (x, y))) 

Because return (x,y) closes on both the surrounding function and the one containing it, it seems that an intermediate monad, as exists on the left side of the associativity law (m >>= f), cannot exist in this example.


Solution

  • Indeed, it's not possible to directly apply the associative law, because of the scope of x in the original expression:

    import Control.Monad (liftM)
    
    test = let m = [1,2]
               ys = [4, 5, 6]
           in m >>= (\x -> ys >>= (\y -> return (x, y)))
    

    However, we can reduce the scope of x if we include it into the result of the first monadic computation. Instead of returning [Int] in x -> ys, we'll use \x -> liftM ((,) x) ys and return [(Int,Int)], where the first number in each pair is always x and the second is one of ys. (Note that for lists liftM is the same as map.) And the second function will read the value of x from its input:

    test1 = let m = [1,2]
                ys = [4, 5, 6]
            in m >>= (\x -> liftM ((,) x) ys >>= (\(x', y) -> return (x', y)))
    

    (The monadic function \(x', y) -> return (x', y) could be now simplified just to return, and subsequently >>= return removed completely, but let's keep it there for the sake of the argument.)

    Now each monadic function is self-contained and we can apply the associativity law:

    test2 = let m = [1,2]
                ys = [4, 5, 6]
            in (m >>= \x -> liftM ((,) x) ys) >>= (\(x, y) -> return (x, y))