首页 文章

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

提问于
浏览
8

我有一个小的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'

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

1 回答

  • 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"]
    

    驱动示例:

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

    我认为这种模式可以抽象成类似的东西

    whileJust :: (Monad m) => Proxy a' a b' b m r -> Proxy a' (Maybe a) b' b m (Maybe r)
    

    所以你可以写

    withEOF P.stdinLn >-> (whileJust counterPipe >>= maybe (return ()) finishCounter) >-> P.stdoutLn
    

    无需更改原始 counterPipe 定义;但我之前从未使用 Pipes (上面的解决方案只是通过查看类型和播放类型 - 多米诺骨牌来计算出来)所以我没有设法写 whileJust (签名可能过于通用,我无法想象出) .

相关问题