有 Haskell 个管道的有状态生成器

Stateful generators with Haskell pipes

  1. 假设我想建模,使用 Haskell 管道,一个 Python Generator[int, None, None] 保持一些内部状态。应该 我正在使用 Producer int (State s) ()StateT s (Producer int m) (),其中 m 是我最终想要的任何类型的效果 消费者?

  2. 我应该如何考虑管道中换能器的概念?所以在 奥列格的simple generators,还有

    type Transducer m1 m2 e1 e2  = Producer m1 e1 -> Producer m2 e2
    

    但我不知道管道中的模拟是什么,因为任何 Proxy 交互的对象似乎依赖于相同的底层 monad m, 没有从 m1 切换到 m2。请参阅 Prelude 函数,因为 实例.

我想我只是误解了有关管道工作方式的一些基本知识。感谢您的帮助。

pipes 中,您通常不会在整体 Effect 的基础 monad m 中使用效果来模拟 内部 Producer 的状态。如果您真的想为此目的使用 State,那将是相关 Producer 的内部实现细节(由 runStatePevalStateP 中的 Producer,如下所述),并且 State 不会出现在 Producer 的类型中。

同样重要的是要强调 Producer,即使它在 Identity 基本 monad 中运行而没有任何“效果”可供其支配,也不是某种纯函数在没有 monadic 帮助的情况下一遍又一遍地产生相同的价值。 Producer 基本上是一个流,它可以使用通常的功能机制(例如,递归)来维护状态。所以,你绝对不需要 State 来让 Producer 有状态。

结果是 Pipes 中的 Python Generator[int, None, None] 的通常模型只是未指定的基 monad m 中的 Monad m => Producer Int m () 多态。只有当 Producer 需要一些外部影响(例如,IO 来访问文件系统)时,您才需要更多的 m(例如,MonadIO m 约束或其他东西)。

举个具体的例子,生成伪随机数的Producer显然有“状态”,但典型的实现是“纯”Producer:

randoms :: (Monad m) => Word32 -> Producer Int m ()
randoms seed = do
  let seed' = 1664525 * seed + 1013904223
  yield $ fromIntegral seed'
  randoms seed'

通过递归维护状态。

如果你真的 决定通过State monad 维持这个状态,Producer 的类型不会改变。您只需在内部使用 State 即可。 Pipes.Lift 模块提供了一些帮助程序(如此处使用的 evalStateP)来在本地添加一个 monad 层以促进此操作:

randoms' :: (Monad m) => Word32 -> Producer Int m ()
randoms' seed = evalStateP seed $ forever $ do
  x <- get
  let x' = 1664525 * x + 1013904223
  yield $ fromIntegral x'
  put x'

Oleg 的简单生成器完全不同。他的生产者和消费者通过 monadic effects only 生产和消费价值,而“monad changing”是实现的核心。特别是,我相信他的消费者和传感器可以 通过单子效应保持状态,就像 State 单子一样,尽管我必须更仔细地观察才能当然。

相比之下,pipes 代理可以产生和消费值并维持独立于底层基础 monad 的内部状态。

最终,pipes 中 Oleg 换能器的模拟就是 Pipe。两者都消耗生产者的价值并向消费者产生价值。 Oleg 的转换器中的 monad 变化只是一个实现细节。