你快到了;你只需要运行状态。
main = do
bs <- L.unpack `fmap` L.getContents
flip execStateT 0 $ runEffect $ produceList bs >-> buzzHash >-> hoist lift stdoutLn
我假设您不想恢复状态,所以我使用execStateT
而不是runStateT
.
这里唯一的好奇心是stdoutLn
被标记为Consumer Word64 IO ()
。所以我用hoist lift
它来制作Consumer Word64 (StateT Word64 IO) ()
系列中的所有东西都a >-> b >-> c
必须在底层 monad 和返回类型中达成一致。
以下是一些可能会节省您时间的进一步评论。首先produceFromList
是each
。
此外,您可以hoist lift
通过重新标记您的stdoutLn
:
stdoutLn :: MonadIO m => Consumer Word64 m ()
stdoutLn = do
a <- await
liftIO $ print a
但是这里有一些麻烦:你没有重复这个动作。这应该很明显是一个循环:
stdoutLn :: MonadIO m => Consumer Word64 m ()
stdoutLn = do
a <- await
liftIO $ print a
stdoutLn
事实上,这已经是可用的了P.print
,所以我们可以写
import qualified Pipes.Prelude as P
main = do
bs <- L.unpack `fmap` L.getContents
flip execStateT 0 $ runEffect $ each bs >-> buzzHash >-> P.print
如果我理解你,buzzHash
也意味着无限期地重复:
buzzHash = do
x <- await
h <- lift $ get -- pull out previous value
let h' = rotate h 1 `xor` (hashArrW8!x) -- calculate new value
lift $ put h' -- save new value
yield h'
buzzHash
(这是forever buzzHash
,我们使用你的地方buzzHash
)
最后,如果你
import qualified Pipes.ByteString as PB
import Control.Lens (view) -- (or Lens.Micro.MTL or Lens.Simple)
我们看到我们不需要惰性字节串 IO,它无论如何都不能正确流式传输。
Pipes.ByteString
已经有了unpack
我们想要的,包装成一个镜头,这样我们就可以view PB.unpack
在其他地方使用B.unpack
。所以最后我们可以写
main = flip evalStateT 0 $ runEffect $ view PB.unpack PB.stdin >-> buzzHash >-> P.print
一旦它是这种形式,我们就会看到我们没有使用管道的底层状态,除了 in buzzHash
,所以我们可以本地化它
import Pipes.Lift (evalStateP)
main = runEffect $ view PB.unpack PB.stdin >-> evalStateP 0 buzzHash >-> P.print
或者,如果你愿意,你可以重写
buzzHash' :: Monad m => Word64 -> Pipe Word8 Word64 m r
buzzHash' n = evalStateP n $ forever $ do
x <- await
h <- lift $ get -- pull out previous value
let h' = rotate h 1 `xor` (hashArrW8!x) -- calculate new value
lift $ put h' -- save new value
yield h'
然后你会写
main = runEffect $ view PB.unpack PB.stdin >-> buzzHash' 0 >-> P.print