应用变形金刚真的是多余的吗?

时间:2016-06-11 07:17:51

标签: haskell stream typeclass

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

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

但我可以定义那些似乎不是应用程序组合的应用变形金刚!例如 sideeffectful streams

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

虽然MonadMStream m个实例,但效率低下。 (甚至不正确?)Applicative实例实际上很有用!

现在请注意,通常的流作为身份仿函数的特殊情况出现:

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

Streamf的构成不是MStream f!相反,Compose Stream f aStream (f a)同构。

我想知道MStream是否是任何两个申请人的组合。

编辑:

我想提供一个类别理论观点。变压器是一个很好的&#34; endofunctor t在应用函子的类别C上(即具有强度的松散幺正仿函数),以及从liftA到{{1}的身份的自然转换C }。更普遍的问题是现在存在哪些有用的变换器不具有与t&#34;组成的形式。 (g是申请人)。我的主张是g就是其中之一。

1 个答案:

答案 0 :(得分:8)

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

  1. 将现有的应用程序或monad编译为更复杂的应用程序或monad。
  2. 从某个给定的起始集构建所有应用程序/ monad。
  3. 广告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 streams data Stream a = Cons a (Stream a)和另一个适用gStream (g a)g (Stream a)都是适用的。

    但即使Stream也是一个monad(join采用二维流的对角线),它与另一个monad m的合成也不会Stream (m a) m (Stream a) 1}}也不会MStream g永远是monad。

    此外,正如我们所看到的,它们与您的f(非常接近ListT done right)不同,因此:

    广告2:所有应用程序都可以使用一组给定的基元构建吗?显然不是。一个问题是构建和数据类型:如果gEither (f a) (g a)是适用的,Right h <*> 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) )然后

    {{1}}