If I have two monads m and n, and n is traversable, do I necessarily have a composite m-over-n monad?
More formally, here's what I have in mind:
import Control.Monad
import Data.Functor.Compose
prebind :: (Monad m, Monad n) =>
m (n a) -> (a -> m (n b)) -> m (n (m (n b)))
mnx `prebind` f = do nx <- mnx
return $ do x <- nx
return $ f x
instance (Monad m, Monad n, Traversable n) => Monad (Compose m n) where
return = Compose . return . return
Compose mnmnx >>= f = Compose $ do nmnx <- mnmnx `prebind` (getCompose . f)
nnx <- sequence nmnx
return $ join nnx
Naturally, this type-checks, and I believe works for a few cases that I checked (Reader over List, State over List) -- as in, the composed 'monad' satisfies the monad laws -- but I'm unsure if this is a general recipe for layering any monad over a traversable one.
No, it's not always a monad. You need extra compatibility conditions relating the monad operations of the two monads and the distributive law sequence :: n (m a) -> m (n a), as described for example on Wikipedia.
Your previous question gives an example in which the compatibility conditions are not met, namely
S = m = [], with unit X -> SX sending x to [x];
T = n = (->) Bool, or equivalently TX = X × X, with unit X -> TX sending x to (x,x).
The bottom right diagram on the Wikipedia page does not commute, since the composition S -> TS -> ST sends xs :: [a] to (xs,xs) and then to the Cartesian product of all pairs drawn from xs; while the right-hand map S -> ST sends xs to the "diagonal" consisting of only the pairs (x,x) for x in xs. It is the same problem that caused your proposed monad to not satisfy one of the unit laws.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With