Monad.Reader Issue 19的“协程管道”文章中,作者定义了一个通用的Coroutine类型:

newtype Coroutine f m a = Coroutine
  { resume :: m (Either (f (Coroutine f m a)) a)
  }

我注意到这种类型与 FreeT 包中的 free 类型非常相似:
data FreeF f a b = Pure a | Free (f b)

newtype FreeT f m a = FreeT
  { runFreeT :: m (FreeF f a (FreeT f m a))
  }

看来FreeTCoroutine是同构的。这是从一个映射到另一个的函数:
freeTToCoroutine
  :: forall f m a. (Functor f, Functor m) => FreeT f m a -> Coroutine f m a
freeTToCoroutine (FreeT action) = Coroutine $ fmap f action
  where
    f :: FreeF f a (FreeT f m a) -> Either (f (Coroutine f m a)) a
    f (Pure a) = Right a
    f (Free inner) = Left $ fmap freeTToCoroutine inner

coroutineToFreeT
  :: forall f m a. (Functor f, Functor m) => Coroutine f m a -> FreeT f m a
coroutineToFreeT (Coroutine action) = FreeT $ fmap f action
  where
    f :: Either (f (Coroutine f m a)) a -> FreeF f a (FreeT f m a)
    f (Right a) = Pure a
    f (Left inner) = Free $ fmap coroutineToFreeT inner

我有以下问题:
  • FreeTCoroutine类型之间的关系是什么?为什么“协程管道”的作者不使用FreeT类型而不是创建Coroutine类型?
  • 自由单子(monad)和协程之间是否存在某种更深层次的关系? 类型是同构的似乎不是巧合。
  • 为什么Haskell中不流行的流库不是基于FreeT

    pipes 的核心数据类型是 Proxy :
    data Proxy a' a b' b m r
      = Request a' (a  -> Proxy a' a b' b m r )
      | Respond b  (b' -> Proxy a' a b' b m r )
      | M          (m    (Proxy a' a b' b m r))
      | Pure    r
    

    conduit 的核心数据类型是 Pipe :
    data Pipe l i o u m r
      = HaveOutput (Pipe l i o u m r) (m ()) o
      | NeedInput (i -> Pipe l i o u m r) (u -> Pipe l i o u m r)
      | Done r
      | PipeM (m (Pipe l i o u m r))
      | Leftover (Pipe l i o u m r) l
    

    我想可以根据Proxy编写PipeFreeT数据类型,所以我想知道为什么不这样做吗?是出于性能原因吗?

    我在流行的流媒体库中看到的唯一FreeT提示是pipes-group,它使用FreeT对流中的项目进行分组。
  • 最佳答案

    为了回答您的第二个问题,让我们首先通过查看Free来简化问题。 Free f a允许您构造f -values的a形AST,以便以后进行归约(又名解释)。将本文中的monad转换器与未提升的自由构造进行比较时,我们可以简单地选择Identity作为m,这是从其转换器构造基本monad的通常做法:Free f = FreeT Identity f

    Monad Reader文章首先介绍了一个提升的蹦床monad变压器,所以让我们从查看未提升的版本开始,其中删除了Identity:

    data Trampoline a = Return a | Bounce (Trampoline a)
    

    如果我们将此与Free进行比较
    data Free f r = Pure r | Free (f (Free f r))
    

    再斜视一下,我们可以看到我们真正需要做的就是“删除” f-结构,就像我们之前“删除”了m -structure一样。因此,我们又有了Trampoline = Free Identity,因为Identity没有结构。反过来,这意味着该蹦床是FreeT Identity Identity:一种简陋形状的简并协程,无法使用效果来确定反弹还是返回。这就是蹦床和蹦床monad变压器之间的区别:该变压器允许弹跳与m -actions交错插入。

    通过一些工作,我们还可以看到,对于f的特定选择(分别为((,) a)((->) a)),生成器和使用者都是免费的monad。他们的免费monad转换器版本也类似地允许他们交错m -actions(例如,生成器可以在屈服之前请求用户输入)。 Coroutine概括了f,AST形状(对于蹦床固定为f ~ Identity)和可以对m ~ Identity交错的效果类型(固定为no effect或Free)。这就是FreeT m f

    直观地,如果Free f是用于纯粹构造f形AST的单子(monad),那么FreeT m f是用于构造与f提供的效果交错的m形AST的单子(monad)。如果您斜视一下,这就是协程的确切含义:完整的泛化,它对构造的AST的形状和构造它的效果类型既参数化的计算进行参数化。

    关于haskell - Haskell的FreeT和协程类型之间有什么关系,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45192066/

    10-10 20:00