There is a lot of talk about Applicative
not needing its own transformer class, like this:
class AppTrans t where
liftA :: Applicative
Great question! I believe there are two different parts of this question:
Ad 1.: Monad transformers are essential for combining monads. Monads don't compose directly. It seems that there needs to be an extra bit of information provided by monad transformers that tells how each monad can be composed with other monads (but it could be this information is already somehow present, see Is there a monad that doesn't have a corresponding monad transformer?).
On the other hand, applicatives compose directly, see Data.Functor.Compose. This is why don't need applicative transformers for composition. They're also closed under product (but not coproduct).
For example, having infinite streams data Stream a = Cons a (Stream a)
and another applicative g
, both Stream (g a)
and g (Stream a)
are applicatives.
But even though Stream
is also a monad (join
takes the diagonal of a 2-dimensional stream), its composition with another monad m
won't be, neither Stream (m a)
nor m (Stream a)
will always be a monad.
Furthermore as we can see, they're both different from your MStream g
(which is very close to ListT done right), therefore:
Ad 2.: Can all applicatives be constructed from some given set of primitives? Apparently not. One problem is constructing sum data types: If f
and g
are applicatives, Either (f a) (g a)
won't be, as we don't know how to compose Right h <*> Left x
.
Another construction primitive is taking a fixed point, as in your MStream
example. Here we might attempt to generalize the construction by defining something like
newtype Fix1 f a = Fix1 { unFix1 :: f (Fix1 f) a }
instance (Functor (f (Fix1 f))) => Functor (Fix1 f) where
fmap f (Fix1 a) = Fix1 (fmap f a)
instance (Applicative (f (Fix1 f))) => Applicative (Fix1 f) where
pure k = Fix1 (pure k)
(Fix1 f) <*> (Fix1 x) = Fix1 (f <*> x)
(which requires not-so-nice UndecidableInstances
) and then
data MStream' f g a = MStream (f (a, g a))
type MStream f = Fix1 (MStream' f)