有一个 Producer ByteString IO ()
类型的生产者和一个 Pipe ByteString a IO ()
类型的管道 我如何组成一个效果,这将导致运行时IO a
?
这是我最好的尝试:
{-# LANGUAGE ScopedTypeVariables #-}
import Pipes
import Data.ByteString
run :: forall a. IO a
run = runEffect $
(undefined :: Producer ByteString IO ()) >-> (undefined :: Pipe ByteString a IO ())
它失败并显示以下内容:
Couldn't match type `Void' with `()'
Expected type: IO a
Actual type: IO ()
In the expression:
runEffect
$ (undefined :: Producer ByteString IO ())
>-> (undefined :: Pipe ByteString a IO ())
In an equation for `run':
run
= runEffect
$ (undefined :: Producer ByteString IO ())
>-> (undefined :: Pipe ByteString a IO ())
通常,您需要使用Consumer
编写Producer
才能获得可由runEffect
运行的Effect
。这不是你在这里得到的,但幸运的是,除了runEffect
之外,还有更多方法可以消除Proxy
。
盘点一下我们所拥有的,这个组合最终会得到一个 Producer
.
pipe :: Producer a IO ()
pipe = (undefined :: Producer ByteString IO ()) >-> (undefined :: Pipe ByteString a IO ())
Pipes.Prelude
模块包含许多其他消除Producers
的方法,例如Pipes.Prelude.last
last :: Monad m => Producer a m () -> m (Maybe a)
可能让a
Pipes.Prelude.fold
的最通用方法是使用
fold :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Producer a m () -> m b
这就像runEffect
,除了它减少了Producers
到它们的基础Monad
。因为这就是我们所拥有的,它会很好用。以下是我们如何实施Pipes.Prelude.head
slowHead = fold (res a -> res <> First (Just a)) mempty getFirst
尽管值得注意的是,slowHead
消耗了整个Producer
(从而执行了所有需要的效果),而Pipes.Prelude.head
只执行了第一个效果。懒多了!