A lot of problems in haskell development is related with the problem of chaining effects. How do I insert an effect in a chain of computations?
Since the chain operation is bind, and has this signature:
(>>=) :: m a -> (a -> m b) -> m b
then the two operands: the result of the chain and the rest of the chain should be in the same monad, so they should have the same effects.
To overcome this problem the monad transformer and the free monad use the "runXXX" and the lift pattern with XXX being the effect desired can run for some time:
runXXX :: t m a -> m a
lift :: m a -> t m a
so I can write: x >>= runXXX myCompXXX >>= y
for inserting XXX effect in a computation that do not use the XXX effect
or runXXXX $ myComp1XXX >>= lift noXXXeffect >>= myCom2XXX
to insert a computation that has not the XXX effect in a chain that uses it.
If I want a computation that uses the XXX effect and a new YYY effect,....
Monad transformers have an inherent limitation: they enforce the static ordering of effect layers and hence statically fixed effect interactions. There are practically significant computations that require interleaving of effects.
http://okmij.org/ftp/Haskell/extensible/index.html#MTL-drawbacks
Effect monads in the other side, allow interleaving of effects, in the sense that they can apply effects in an arbitrary order but since bind is defined as is, the effect signature to the runXXX primitive.
runXXX :: n a -> m a
a typical example using type level lists
runState :: m effect a -> stateValue -> m(state:effects) (a, stateValue')
set :: m effect a -> m(state:effect) a get :: m (Has state effects) -> m effects x
to use set and get I need runState as defined above so I can write:
x >>= runState (set "hello" >>= get) >>= y
Essentially the problem is the same that in a transformer
However if bind is redefined as: >>=' :: a -> (a -> n b) -> m b
where the monad of the result may have a different type and thus, can express the new effect without using a runner, since I can use set and get without the runner:
x >>=' startEffect "hello >>=' put "world" >>=' get >>=' y
so the signature of startEffect:
startEffect :: (NotMember state effects) -> m (state:effects) ()
and the whole operation once set is executed would have the state effect until the end of the computation.
There is no need for lifters and runners.
This in a conventional stack using the standard bind would be semantically equivalent to:
x >>= runState "hello ( set "world" >>= get >>= lift y)
To be continued