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.