I have recently started learning Haskell, and I was trying to do the following function composition (join . mapM) but got some weird types out of this function that I don't understand. I thought that either GHC would assume that t == m in the mapM type and the output of mapM would become m (m b) which would be join-able or it would not and this would error out because of type mismatch. Instead the following happened:
mapM :: (Traversable t, Monad m) => (a -> m b) -> t a -> m (t b)
join :: Monad m => m (m a) -> m a
join . mapM :: Traversable t => (a -> t a -> b) -> t a -> t b
I don't understand how this is possible. The way I understand it, function composition should use the inputs of the first (or the second depending how you look at it) function and the outputs of the second function. But here the expected input function for mapM changes from a unary function to a binary function and I have no clue why. Even if GHC can't just make the assumption that t == m like I did, which I half expected, it should error out because of type mismatch, not change the input function type, right? What is happening here?
liftM, akafmap, seems unrelated to this composition). But in short, the output ofmapMis a functiont a -> m (t b), andjoinneeds a monadic value of the formm ( m c)as input. The only way for these types to unify is for the Monadmto be the function typet a -> b, wheretandaare fixed. (It's actually rather surprising to me that this works at all, but the types all match up.)(->) t a, whose values are functions with inputt a. (Sometime I wonder why Haskell has both a direct Monad instance for "functions with a fixed input", and one called the "reader monad" which is exactly the same but in anewtypewrapper. I think it would work better without the "unwrapped" version having a monad/applicative instance - but I'm getting off topic...)mapMist a -> m (t b). Ifmis the monad(->) (t a)then that's the same ast a - > t a -> t b- orm (m (t b))for the same monad.jointurns that intom (t b), ort a -> t b.