应用变压器是否真的是多余的? [英] Are applicative transformers really superfluous?

查看:120
本文介绍了应用变压器是否真的是多余的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有很多关于 Applicative not 的讨论需要它自己的变换器类,如下所示:

  class AppTrans t其中
liftA :: Applicative f => f a - > t f a

但我可以定义应用变形金刚,看起来不是应用变体的组合!例如 sideeffectful streams

  data MStream fa = MStream(f(a,MStream fa)) 

提升仅在每一步执行副作用:

 实例AppTrans MStream其中
liftA action = MStream $(,)< $>动作< *> pure(liftA action)

如果 f 是一个应用程序,那么 MStream f 也是:​​

  instance Functor f => Functor(MStream f)其中
fmap fun(MStream stream)= MStream $(\(a,as) - >(fun a,fmap fun as))< $>流

实例f => Applicative(MStream f)其中
pure = liftA。纯
MStream fstream< *> MStream astream = MStream
$(\(f,fs)(a,as) - >(f a,fs * as))< $> fstream< *> astream

我知道任何实际用途, f 应该是monad:

  joinS :: Monad m => MStream m a  - > m [a] 
joinS(MStream stream)= do
(a,as)< - stream
aslist< - joinS as
return $ a:aslist

但是当<$ c c> Monad 实例$ c> MStream m ,效率不高。 (或者甚至是不正确的?) Applicative 实例实际上是有用的!

现在请注意,通常的流作为特例对于身份仿函数:

pre $ $ $ c $ import import Data.Functor.Identity
类型Stream a = MStream Identity a

但是 Stream f 不是 MStream f !相反, Compose Stream fa 同构于 Stream(fa)



我想知道 MStream 是否是任何两个应用程序的组合。



编辑:



我想提供一个类别理论观点。变形是应用函子的类别 C 上的一个很好的endofunctor t >(即具有强度的松散monoidal函子) ,以及从 C t liftA $ C>。更普遍的问题是,现在有哪些有用的转换器不是与 g (其中 g 是一个应用)。我的观点是 MStream 就是其中之一。

!我相信这个问题有两个不同的部分:


  1. 将现有的应用程序或monad组合成更复杂的应用程序。

  2. 构建所有应用程序/ monad。 p> Ad 1. .: Monad变压器对于组合单声道是必不可少的。 Monads 不要直接撰写 >。似乎需要由monad变换器提供的额外信息来说明每个monad如何与其他monad组合(但它可能是这种信息已经存在,请参阅是否有monad没有相应的monad变压器?)。

    另一方面, 应用程序直接撰写,请参阅 Data.Functor.Compose 。这就是为什么不需要应用变压器的组成。他们也在产品(但不是 coproduct ) 。



    例如, infinite streams data Stream a = Cons a(Stream a)和另一个应用 g ,都是 Stream(ga) g(Stream a)是适用的。



    但即使 Stream 也是monad( join )的对角线一个二维流),它与另一个monad m 的组合将不会是,既不是 Stream(ma)也不是 m(Stream a)将永远是一个单子。



    此外,正如我们所看到的,它们都不同F从你的 MStream g (这非常接近 ListT done right ),因此:



    广告2:所有应用程序是否可以从一些给定的基元集?显然不是。一个问题是构造和数据类型:如果 f g 是可应用的,则 Either(fa )(ga)不会,因为我们不知道如何撰写右侧h<>左x



    另一个构造原语采用了一个固定点,如 MStream 例子。在这里,我们可以尝试通过定义类似于

      newtype Fix1 fa = Fix1 {unFix1 :: f(Fix1 f) a} 

    instance(Functor(f(Fix1 f)))=> Funder(Fix1 f)其中
    fmap f(Fix1 a)= Fix1(fmap f a)

    instance(Applicative(f(Fix1 f)))=> Applicative(Fix1 f)其中
    pure k = Fix1(纯k)
    (Fix1 f)* (Fix1x)= Fix1(f * x)

    (它不需要-nice UndecidableInstances )然后是

      data MStream'fga = MStream f(a,ga))

    类型MStream f = Fix1(MStream'f)


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

    class AppTrans t where
        liftA :: Applicative f => f a -> t f a
    

    But I can define applicative transformers that don't seem to be compositions of applicatives! For example sideeffectful streams:

    data MStream f a = MStream (f (a, MStream f a))
    

    Lifting just performs the side effect at every step:

    instance AppTrans MStream where
        liftA action = MStream $ (,) <$> action <*> pure (liftA action)
    

    And if f is an applicative, then MStream f is as well:

    instance Functor f => Functor (MStream f) where
        fmap fun (MStream stream) = MStream $ (\(a, as) -> (fun a, fmap fun as)) <$> stream
    
    instance Applicative f => Applicative (MStream f) where
        pure = liftA . pure
        MStream fstream <*> MStream astream = MStream
            $ (\(f, fs) (a, as) -> (f a, fs <*> as)) <$> fstream <*> astream
    

    I know that for any practical purposes, f should be a monad:

    joinS :: Monad m => MStream m a -> m [a]
    joinS (MStream stream) = do
        (a, as) <- stream
        aslist <- joinS as
        return $ a : aslist
    

    But while there is a Monad instance for MStream m, it's inefficient. (Or even incorrect?) The Applicative instance is actually useful!

    Now note that usual streams arise as special cases for the identity functor:

    import Data.Functor.Identity
    type Stream a = MStream Identity a
    

    But the composition of Stream and f is not MStream f! Rather, Compose Stream f a is isomorphic to Stream (f a).

    I'd like to know whether MStream is a composition of any two applicatives.

    Edit:

    I'd like to offer a category theoretic viewpoint. A transformer is a "nice" endofunctor t on the category C of applicative functors (i.e. lax monoidal functors with strength), together with a natural transformation liftA from the identity on C to t. The more general question is now what useful transformers exist that are not of the form "compose with g" (where g is an applicative). My claim is that MStream is one of them.

    解决方案

    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)
    

    这篇关于应用变压器是否真的是多余的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆