首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如果MonadPlus是“生成器”类,那么“使用者”类是什么?

如果MonadPlus是“生成器”类,那么“使用者”类是什么?
EN

Stack Overflow用户
提问于 2014-07-31 23:46:37
回答 1查看 562关注 0票数 14

Pipe可以分为两部分:生成器部分(yield)和使用者部分(await)。

如果您的Pipe只使用它的生成器一半,并且只返回() (或从不返回),那么它可以表示为“正确完成的ListT”。事实证明,MonadPlus可以用来表示类似于ListT完成的任何东西-对。引用加布里埃尔·冈萨雷斯的话

请注意,您可以构建仅依赖于转换器的任何ListT (不仅仅是管道中的)。例如,下面是如何实现ListT模拟的Pipes.Prelude.stdinLn: -- stdinLn ::ListT IO字符串stdinLn ::(MonadTrans t,MonadPlus (t IO)) => t IO String stdinLn = do eof <- lift isEOF如果eof的其他操作为str <- lift getLine返回str mplus stdinLn 它将作为所有的ListT类型进行检查,并为所有这些都做正确的事情。

因此,我的问题是:对于管道的消费者部分,ListTMonadPlus是否具有双重特性?

要求:

  • 不使用yield,只返回() (或从不返回)但使用await的管道可以表示为“双到ListT”。
  • “对偶到ListT”可以推广到“MonadPlus的对偶”
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-08-01 14:55:44

我认为答案不是将“生成器”类型类二元化,而是使用一个简单的Category实例来扩展它,该实例相当于await/(>~)pipes

不幸的是,无法安排类型变量使其满足所有三个类型类(MonadPlusMonadTransCategory),因此我将定义一个新的类型类:

代码语言:javascript
复制
{-# 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

这类的法律是类别法:

代码语言:javascript
复制
await >~ f = f

f >~ await = f

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

有了这个额外的类型类之后,您就可以实现ConsumerPipe了:

代码语言:javascript
复制
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添加新类型类的情况下解决如何做到这一点。如果可能的话,我更希望重用原始的Category类型类,可能的话,await(>~)只是将您的类型包装在一个newtype中的函数,使用Category实例,然后展开它,但是我仍然在研究如何这样做的细节。

编辑:我找到了解决方案。只需定义以下newtype:

代码语言:javascript
复制
{-# 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)

然后,任何库都可以为其类型实现一个Category实例,该实例包装在Consumer新类型中。

然后,无论何时使用await(>~),都会得到这样的约束。

代码语言:javascript
复制
cat :: (MonadPlus (t a m), Category (Consumer t m)) => t a m a
cat = await `mplus` cat
票数 10
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25070740

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档