MonadTrans is abused here. One could argue that none of the MonadTrans laws hold for the instance of GenericLangM. First of all, MonadTrans itself did not explicitly require a Monad instance of its outer class (i.e. it was defined as class MonadTrans t rather than class Monad t => MonadTrans t). Of course the question arises what does this make of the laws in this case:
lift . return = return: return is not defined, as GenericLangM l m is no Monad, but return is just a synonym for pure. So it would be possible to argue that it sort of holds as lift . pure = pure does hold. But to say the least we can say that it does not defy the law as it is not possible to write return (but lift . return is).
lift (m >>= f) = lift m >>= (lift . f): Again, >>= is not defined, argument passing using bind is not possible, i.e. it is not possible to write lift m >>= (lift . f), so it might not be that the law holds; but it does not defy it either. The closest we could write is lift (m *> f) = lift m *> lift f which would hold.
The reasons why it exists might be weak:
compatibility with older code
convenience
Nevertheless we need an alternative as @jvoigtlaender pointed out:
MonadTrans (GenericLangM l) seems to indeed require that forall m. Monad m => Monad (GenericLangM l m)
Therefore the proposal is to define FunctorTrans class within output-monad having the same function lift but defining the above stated (modified) laws. In turn it would then be required to import this other lift when writing output-monad code. Confusing both would be no issue, as the first law behaves equivalent and the second law is different in the sense that f is of type a -> m b while in the FunctorTrans case of type f b, i.e. the compiler would throw errors if the "wrong" lift would be used. GenericLangM could would require FunctorLift because of its type.
And why do we require lift at all? In order to be able to have (restricted) IO-computations, that can be lifted into the inner monad (like reading or computing image files).
MonadTrans
is abused here. One could argue that none of theMonadTrans
laws hold for the instance ofGenericLangM
. First of all,MonadTrans
itself did not explicitly require aMonad
instance of its outer class (i.e. it was defined as classMonadTrans t
rather than classMonad t => MonadTrans t
). Of course the question arises what does this make of the laws in this case:lift . return = return
:return
is not defined, asGenericLangM l m
is noMonad
, butreturn
is just a synonym forpure
. So it would be possible to argue that it sort of holds aslift . pure = pure
does hold. But to say the least we can say that it does not defy the law as it is not possible to writereturn
(butlift . return
is).lift (m >>= f) = lift m >>= (lift . f)
: Again,>>=
is not defined, argument passing using bind is not possible, i.e. it is not possible to writelift m >>= (lift . f)
, so it might not be that the law holds; but it does not defy it either. The closest we could write islift (m *> f) = lift m *> lift f
which would hold.The reasons why it exists might be weak:
Nevertheless we need an alternative as @jvoigtlaender pointed out:
Therefore the proposal is to define
FunctorTrans
class withinoutput-monad
having the same functionlift
but defining the above stated (modified) laws. In turn it would then be required to import this other lift when writingoutput-monad
code. Confusing both would be no issue, as the first law behaves equivalent and the second law is different in the sense thatf
is of typea -> m b
while in theFunctorTrans
case of typef b
, i.e. the compiler would throw errors if the "wrong" lift would be used.GenericLangM
could would requireFunctorLift
because of its type.And why do we require lift at all? In order to be able to have (restricted) IO-computations, that can be lifted into the inner monad (like reading or computing image files).