【问题标题】:Are applicative transformers really superfluous?应用变压器真的是多余的吗?
【发布时间】:2016-10-12 04:18:26
【问题描述】:

有很多关于Applicative不需要需要自己的转换器类的讨论,像这样:

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

但我可以定义看起来不是应用程序组合的应用程序转换器!例如副作用流:

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

提升只是在每一步都会产生副作用:

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

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

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

我知道出于任何实际目的,f 应该是一个 monad:

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

但是虽然MStream m 有一个Monad 实例,但它的效率很低。 (甚至不正确?)Applicative 实例实际上很有用!

现在请注意,通常的流是作为恒等函子的特殊情况出现的:

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

但是Stream和f的组成不是MStream f!相反,Compose Stream f a 与 Stream (f a) 同构。

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

编辑:

我想提供一个范畴论的观点。 Transformer 是应用函子类别C 上的“不错”内函子t(即具有强度的松散单曲面函子),以及从C 上的身份到t 的自然转换liftA。现在更普遍的问题是存在哪些有用的转换器不是“与g 组合”形式(其中g 是一个应用程序)。我的主张是MStream 就是其中之一。

【问题讨论】:

    标签: haskell stream typeclass


    【解决方案1】:

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

    1. 组合现有的应用程序或单子到更复杂的应用程序或单子。
    2. 从一些给定的起始集构建所有应用程序/单子。

    广告 1.:Monad 转换器对于组合 monad 至关重要。 Monads don't compose directly。似乎需要 monad 转换器提供额外的信息,说明每个 monad 如何与其他 monad 组合(但可能这些信息已经以某种方式存在,请参阅Is there a monad that doesn't have a corresponding monad transformer?)。

    另一方面,应用程序直接组合,请参阅Data.Functor.Compose。这就是为什么不需要应用转换器来组合。他们也在product(但不是coproduct)下关闭。

    例如,拥有infinite streamsdata Stream a = Cons a (Stream a) 和另一个应用程序g,Stream (g a) 和g (Stream a) 都是应用程序。

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

    此外,正如我们所见,它们都与您的MStream g 不同(与ListT done right 非常接近),因此:

    广告 2.:所有应用程序都可以由一组给定的原语构造吗?显然不是。一个问题是构造 sum 数据类型:如果 f 和 g 是应用程序,Either (f a) (g a) 不会是,因为我们不知道如何编写 Right h &lt;*&gt; Left x。

    另一个构造原语采用固定点,如您的MStream 示例。在这里,我们可能会尝试通过定义类似的东西来概括构造

    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)
    

    (这需要不太好的UndecidableInstances)然后

    data MStream' f g a = MStream (f (a, g a))
    
    type MStream f = Fix1 (MStream' f)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-02
      • 2018-11-09
      • 2016-01-07
      • 2018-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多