listhaskellmonadsfunction-compositionkleisli

Understanding monadic function composition


I am learning about monads from the book 'Learn You a Haskell for Great Good!' by Miran Lipovaca. I am trying to understand the associativity law for monads. Essentially, the law states that when you have a chain of monadic function applications with >>=, it shouldn't matter how they're nested.

The following code enables one to pass the result of a function of type a -> m b to a function of type b -> m c:

(<=<) :: (Monad m) => (b -> m c) -> (a -> m b) -> (a -> m c)
f <=< g = (\x -> g x >>= f)

However, for the example below:

ghci> let f x = [x, -x]
ghci> let g x = [x*3, x*2]
ghci> let h = f <=< g
ghci> h 3
[9, -9, 6, -6]

Are f x and g x both functions? It seems to be that they are lists with different values of x and not functions. How does the line let h = f <=< g work in the above code? f and g have to be functions since they are used with <=< but I am not sure what they are.


Solution

  • f x = [x, -x]
    

    This is ordinary function definition syntax. We are defining a new function f, by writing down what it would produce when applied to a hypothetical value x.

    let (whether as a statement or a let ... in ... expression) just introduces a block where you can make definitions, much like where. The definitions themselves use the same syntax as global ones do.

    If you know how to define functions by writing e.g. plusOne n = n + 1 in a file, then this syntax is exactly the same (if you don't know how to do that, then I'd suggest reading through some introductory tutorials on fundamental Haskell syntax before you try to understand monadic function composition).

    So after those definitions f and g are functions. f x and g x don't really make sense, since you don't have an x in scope to apply them to.

    If you did have such a value in scope, then f x would be an expression that evaluates to a list, which involves calling the function f. It still wouldn't be true to say that f x or g x are functions.

    So now it should be clear that let h = f <=< g is defining a new value h by applying the <=< operator to f and g.