如何检测Haskell管道中的最后一个块?

Ana*_*Ana 8 haskell haskell-pipes

我有一个小的Haskell Pipe打印出它运行了多少次:

counterPipe :: Pipe String String IO r
counterPipe = go 0
  where
    go n = do
      await >>= yield
      let n' = succ n
      liftIO $ putStrLn $ "Chunk " ++ show n'
      go n'
Run Code Online (Sandbox Code Playgroud)

我希望能够在处理完最后一个块后打印出一条消息,并可能执行其他任务.我该怎么做呢?

Cac*_*tus 3

我可以通过将counterPipe的输入类型更改为并在上游管道完成后Maybe String注入额外的内容来使其工作:Nothing

import Pipes
import Pipes.Core (respond)
import Control.Applicative ((<*))

withEOF :: (Monad m) => Proxy a' a b' b m r -> Proxy a' a b' (Maybe b) m r
withEOF p = for p (respond . Just) <* respond Nothing

counterPipe :: Pipe (Maybe String) String IO Int
counterPipe = go 0
  where
    go n = do
        mx <- await

        case mx of
            Just x -> do
                yield x
                let n' = succ n
                liftIO $ putStrLn $ "Chunk " ++ show n'
                go n'
            Nothing -> do
                return n

finishCounter :: Int -> Pipe a b IO ()
finishCounter n = liftIO $ putStrLn $ unwords ["Finished after", show n, "chunks"]
Run Code Online (Sandbox Code Playgroud)

示例驱动程序:

import qualified Pipes.Prelude as P
main = runEffect $ withEOF P.stdinLn >-> (counterPipe >>= finishCounter) >-> P.stdoutLn
Run Code Online (Sandbox Code Playgroud)

我认为这种模式应该抽象成类似的东西

whileJust :: (Monad m) => Proxy a' a b' b m r -> Proxy a' (Maybe a) b' b m (Maybe r)
Run Code Online (Sandbox Code Playgroud)

所以你可以写

withEOF P.stdinLn >-> (whileJust counterPipe >>= maybe (return ()) finishCounter) >-> P.stdoutLn
Run Code Online (Sandbox Code Playgroud)

不必改变你原来的counterPipe定义;但我Pipes以前从未使用过(上面的解决方案是通过查看类型和玩类型多米诺骨牌来找出的),所以我还没有成功编写whileJust(签名可能太通用了,我无法弄清楚) 。