There is a lot of talk about Applicative not needing its own transformer class, like this:
class AppTrans t where
liftA :: Applicative f => f a -> t f a
But I can define applicative transformers that don't seem to be compositions of applicatives! For example sideeffectful streams:
data MStream f a = MStream (f (a, MStream f a))
Lifting just performs the side effect at every step:
instance AppTrans MStream where
liftA action = MStream $ (,) <$> action <*> pure (liftA action)
And if f is an applicative, then MStream f is as well:
instance Functor f => Functor (MStream f) where
fmap fun (MStream stream) = MStream $ (\(a, as) -> (fun a, fmap fun as)) <$> stream
instance Applicative f => Applicative (MStream f) where
pure = liftA . pure
MStream fstream <*> MStream astream = MStream
$ (\(f, fs) (a, as) -> (f a, fs <*> as)) <$> fstream <*> astream
I know that for any practical purposes, f should be a monad:
joinS :: Monad m => MStream m a -> m [a]
joinS (MStream stream) = do
(a, as) <- stream
aslist <- joinS as
return $ a : aslist
But while there is a Monad instance for MStream m, it's inefficient. (Or even incorrect?) The Applicative instance is actually useful!
Now note that usual streams arise as special cases for the identity functor:
import Data.Functor.Identity
type Stream a = MStream Identity a
But the composition of Stream and f is not MStream f! Rather, Compose Stream f a is isomorphic to Stream (f a).
I'd like to know whether MStream is a composition of any two applicatives.
Edit:
I'd like to offer a category theoretic viewpoint. A transformer is a "nice" endofunctor t on the category C of applicative functors (i.e. lax monoidal functors with strength), together with a natural transformation liftA from the identity on C to t. The more general question is now what useful transformers exist that are not of the form "compose with g" (where g is an applicative). My claim is that MStream is one of them.