Why is the cost of returning the elevator not a monad?

Why is not MonadTrans defined as

 class MonadTrans t where lift :: (Monad m, Monad (tm)) => ma -> tma -- ^^^^^^^^^^^ 

instead of the current

 class MonadTrans t where lift :: Monad m => ma -> tma 

This is Haskell 98 (as opposed to the proposal Why don’t transformer monads have a chance of a monad coming out? ) And ensures that the result is always a monad. Is there a reason why monad transformers are allowed to create something that is not a monad?

+6
source share
3 answers

I assume that MonadTrans convert a Monad to something else, instead of converting Monad to Monad . It is more general, since you can write something that converts Monad , and you can define lift , but you cannot define >>= and return . Since most (if not all) MonadTrans instances end up being Monad s, this really does not present a problem, since the compiler still handles it just fine.

+4
source

Bheklilr's answer gave me an idea of ​​an example in which a monad transformer creates something that is not a monad. A well-known example of something that is not a monad is a ZipList . And we can make a variant that performs a monadic action at each level:

 import Control.Applicative import Control.Arrow ((***)) import Control.Monad import Control.Monad.Trans -- | A list where each step is produced by a monadic action. data ListT ma = Nil | Cons (m (a, ListT ma)) 

This is actually a monad stream. And it can be easily turned into Functor and Applicative

 instance Monad m => Functor (ListT m) where fmap f Nil = Nil fmap f (Cons k) = Cons $ (f *** fmap f) `liftM` k instance Monad m => Applicative (ListT m) where pure x = Cons $ return (x, pure x) Cons mf <*> Cons mx = Cons $ do (f, fs) <- mf (x, xs) <- mx return (fx, fs <*> xs) _ <*> _ = Nil 

but obviously not a monad. So, we have a MonadTrans instance that converts the monad into something that is only Applicative .

 instance MonadTrans ListT where lift mx = Cons $ (\x -> (x, lift mx)) `liftM` mx 

(All this made me realize that the experimental ZipSink in the optional cable is also a good example.)


However, this raises another question: if we want such transformers, what laws should they adhere to? Laws for MonadTrans defined as

 lift . return = return lift (m >>= f) = lift m >>= (lift . f) 

So, in our case, we might wish for something like

 lift (f `liftM` x) = fmap f (lift x) lift . return = pure lift (m `ap` f) = lift m <*> lift f 
+6
source

I am going to disagree with the other two answers to say that the result should be a monad. The reason is that otherwise there are no reasonable laws to which lift must obey.

It is assumed that

lift are monadic morphisms, which means that it must obey the following two laws:

 lift (return x) = return x lift (m >>= f) = lift m >>= \r -> lift (fr) 

These laws make more sense when you realize that they are functor laws between the two Claysley categories:

 -- ie "fmap id = id" (lift .) return = return -- ie "fmap (f . g) = fmap f . fmap g" (lift .) (f >=> g) = (lift .) f >=> (lift .) g 

However, if you do not limit the conclusion as a monad, then these laws are no longer valid, and you do not have a reasonable way to verify the implementation of lift .

I suspect the real reason was to make the Haskell98 class

+3
source

Source: https://habr.com/ru/post/952669/


All Articles