Are applicative transformers really superfluous?

后端 未结 1 2010
面向向阳花
面向向阳花 2021-02-02 10:52

There is a lot of talk about Applicative not needing its own transformer class, like this:

class AppTrans t where
    liftA :: Applicative          


        
相关标签:
1条回答
  • 2021-02-02 11:45

    Great question! I believe there are two different parts of this question:

    1. Composing existing applicatives or monads into more complex ones.
    2. Constructing all applicatives/monads from some given starting set.

    Ad 1.: Monad transformers are essential for combining monads. Monads don't compose directly. It seems that there needs to be an extra bit of information provided by monad transformers that tells how each monad can be composed with other monads (but it could be this information is already somehow present, see Is there a monad that doesn't have a corresponding monad transformer?).

    On the other hand, applicatives compose directly, see Data.Functor.Compose. This is why don't need applicative transformers for composition. They're also closed under product (but not coproduct).

    For example, having infinite streams data Stream a = Cons a (Stream a) and another applicative g, both Stream (g a) and g (Stream a) are applicatives.

    But even though Stream is also a monad (join takes the diagonal of a 2-dimensional stream), its composition with another monad m won't be, neither Stream (m a) nor m (Stream a) will always be a monad.

    Furthermore as we can see, they're both different from your MStream g (which is very close to ListT done right), therefore:

    Ad 2.: Can all applicatives be constructed from some given set of primitives? Apparently not. One problem is constructing sum data types: If f and g are applicatives, Either (f a) (g a) won't be, as we don't know how to compose Right h <*> Left x.

    Another construction primitive is taking a fixed point, as in your MStream example. Here we might attempt to generalize the construction by defining something like

    newtype Fix1 f a = Fix1 { unFix1 :: f (Fix1 f) a }
    
    instance (Functor (f (Fix1 f))) => Functor (Fix1 f) where
        fmap f (Fix1 a) = Fix1 (fmap f a)
    
    instance (Applicative (f (Fix1 f))) => Applicative (Fix1 f) where
        pure k = Fix1 (pure k)
        (Fix1 f) <*> (Fix1 x) = Fix1 (f <*> x)
    

    (which requires not-so-nice UndecidableInstances) and then

    data MStream' f g a = MStream (f (a, g a))
    
    type MStream f = Fix1 (MStream' f)
    
    0 讨论(0)
提交回复
热议问题