当前位置: 首页 > 知识库问答 >
问题:

如果MonadPlus是“generator”类,那么“consumer”类是什么?

胡锋
2023-03-14

管道可以分为两部分:发电机部分(产量)和消费者部分(等待)。

如果你有一个Pipe,它只使用它的生成器的一半,并且只返回()(或者从不返回),那么它可以表示为"ListT做对了"。事实证明,MonadPlus可以用来表示任何类似ListT-dod-right的东西。引用加布里埃尔·冈萨雷斯的话:

请注意,您可以仅使用变压器依赖项构建任何ListT(不仅仅是管道中的那个)。例如,下面是如何实现Pipes的ListT模拟。Prelude.stdinLn

-- stdinLn :: ListT IO String
stdinLn :: (MonadTrans t, MonadPlus (t IO)) => t IO String
stdinLn = do
    eof <- lift isEOF
    if eof
        then mzero
        else do
            str <- lift getLine
            return str `mplus` stdinLn

这将在那里键入check作为anyListT,并为所有人做正确的事情。

所以我的问题是:对于管道的消费者部分,是否有一个对偶到ListT和到MonadPlus

要求:

  • 从不使用yield,只返回()(或从不返回),但使用wait的管道可以表示为“双重到列表”
  • “dual to ListT”可以概括为“MonadPlus的dual”

共有1个答案

施恩
2023-03-14

我认为答案不是双重化类似生成器的类型类,而是用一个简单的类别实例来扩展它,等效于wait/

不幸的是,没有办法安排类型变量使其满足所有三个类型类(MonadPlusMonadTrans、和Category),因此我将定义一个新的类型类:

{-# LANGUAGE KindSignatures #-}

import Control.Monad
import Control.Monad.Trans.Class

class Consumer (t :: * -> (* -> *) -> * -> *) where
    await :: t a m a
    (>~)  :: t a m b -> t b m c -> t a m c

该类型类别的规则为类别规则:

await >~ f = f

f >~ await = f

(f >~ g) >~ h = f >~ (g >~ h)

然后,一旦有了这个额外的类型类,就可以实现消费者s和管道s:

printer :: (Show a, Monad (t a IO), MonadTrans (t a), Consumer t) => t a IO r
printer = do
    a <- await
    lift (print a)
    printer
{-
printer :: Show a => Consumer a IO r
printer = do
    a <- await
    lift (print a)
    printer
-}

cat :: (MonadPlus (t a m), Consumer t) => t a m a
cat = await `mplus` cat
{-
cat :: Monad m => Pipe a a m r
cat = do
    a <- await
    yield a
    cat
-}

debug :: (Show a, MonadPlus (t a IO), MonadTrans (t a), Consumer t) => t a IO a
debug = do
    a <- await
    lift (print a)
    return a `mplus` debug
{-
debug :: Show a => Pipe a a IO r
debug = do
    a <- await
    lift (print a)
    yield a
    debug
-}

taker :: (Consumer t, MonadPlus (t a m)) => Int -> t a m a
taker 0 = mzero
taker n = do
    a <- await
    return a `mplus` taker (n - 1)
{-
taker :: Monad m => Int -> Pipe a a m ()
taker 0 = return ()
taker n = do
    a <- await
    yield a
    taker (n - 1)
-}

困难的部分是弄清楚如何在不向base添加新类型类的情况下做到这一点。如果可能的话,我更喜欢重用原始的类别类型类,可能有等待

编辑:我找到了解决办法。只需定义以下新类型:

{-# LANGUAGE KindSignatures, FlexibleContexts #-}

import Control.Category
import Prelude hiding ((.), id)

newtype Consumer t m a b = Consumer { unConsumer :: t a m b }

await :: Category (Consumer t m) => t a m a
await = unConsumer id

(>~) :: Category (Consumer t m) => t a m b -> t b m c -> t a m c
f >~ g = unConsumer (Consumer f >>> Consumer g)

然后,任何库都可以为包装在消费者新类型中的类型实现一个类别实例。

然后,在使用wait

cat :: (MonadPlus (t a m), Category (Consumer t m)) => t a m a
cat = await `mplus` cat

 类似资料:
  • 我试图理解背后的动机。如果已经存在类型类和,为什么有必要呢? 诚然,的实例是具体的类型,而的实例需要一个类型参数。(有关有用的解释,请参见Monoid vs MonadPlus。)但是你不能重写任何类型的约束吗 作为和的组合? 从。它的实施是: 我只能使用和实现它: 有人能澄清和之间的真正区别吗?

  • 通过上面的介绍,终于模模糊糊的带到元类这里来了。可是我们到现在还不知道元类是什么鬼东西。 我们创建类的时候,大多数是为了创建类的实例对象。 那么元类呢? 元类就是用来创建类的。也可以换个理解方式就是:元类就是类的类。 通过上面 type() 函数的介绍,我们知道可以通过 type() 函数创建类: MyClass = type('MyClass', (), {}) 实际上 type() 函数是一

  • 在大多数面向对象的编程语言中,类即为包含一系列函数的用户自定义类型。正如我们看到的这样,C++中的结构体就符合这样的定义。 但C++中有另外的结构也符合这一定义;说起来有点令人迷惑,这一结构就是类(class)。 在C++中,类就是变量默认为私有的结构体。举例来说,我可以把“纸牌”结构体定义改成这样。 class Card { int suit, rank; public:

  • 问题内容: 已经了解Python是一种解释性语言…但是,当我查看Python源代码时,我看到的是Windows标识为“编译的Python文件”的文件。这些从哪里来? 问题答案: 它们包含字节码,这是Python解释器将源代码编译到的字节码。然后,此代码由Python的虚拟机执行。 Python的文档解释了这样的定义: Python是一种解释型语言,与编译型语言相反,尽管由于字节码编译器的存在,两者

  • 问题内容: 我们将注释定义为接口,如下所示 而且我们知道所有注释默认都会扩展接口。 当我检查接口的Java库时,我发现它覆盖了Object类等许多方法。 如果Annotation是一个接口,那么它如何扩展Object类并覆盖其方法?接口只能扩展其他接口,不能扩展类。 问题答案: 所以我的问题是,如果Annotation是一个接口,那么它将如何扩展Object类并覆盖其方法 不完全是。Java语言规

  • 我看到这里 老实说,我不理解这张纸条。它们是否意味着有可能用MonadPlus永远中断,例如IO Bool?比如说,IO False将破坏它。。。 当然,我可以破例或者永远实现自己的,但我的兴趣是关于这个奇怪的音符。