fmidue / output-blocks

0 stars 0 forks source link

Define and Use Alternative to `MonadTrans` #6

Closed marcellussiegburg closed 6 months ago

marcellussiegburg commented 8 months ago

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:

  1. 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).
  2. 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:

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)

based on this: https://hackage.haskell.org/package/transformers-0.6.1.1/docs/src/Control.Monad.Trans.Class.html#MonadTrans

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).

marcellussiegburg commented 8 months ago

(see also discussion starting here: https://github.com/fmidue/logic-tasks/pull/78/files/c682e974459eb03d16327de779ba7b9b4b03eb3f#r1452553964)