current version of the Pipes tutorial在以下一个示例中使用以下两个函数:
stdout :: () -> Consumer String IO r
stdout () = forever $ do
str <- request ()
lift $ putStrLn str
stdin :: () -> Producer String IO ()
stdin () = loop
where
loop = do
eof <- lift $ IO.hIsEOF IO.stdin
unless eof $ do
str <- lift getLine
respond str
loop
正如本教程本身所述,由于需要检查输入结束,P.stdin有点复杂。
有没有很好的方法可以重写P.stdin,不需要手动尾递归循环,并使用像P.stdout那样的高阶控制流组合器?在命令式语言中,我将使用结构化的while循环或break语句来执行相同的操作:
while(not IO.isEOF(IO.stdin) ){
str <- getLine()
respond(str)
}
forever(){
if(IO.isEOF(IO.stdin) ){ break }
str <- getLine()
respond(str)
}
答案 0 :(得分:11)
我更喜欢以下内容:
import Control.Monad
import Control.Monad.Trans.Either
loop :: (Monad m) => EitherT e m a -> m e
loop = liftM (either id id) . runEitherT . forever
-- I'd prefer 'break', but that's in the Prelude
quit :: (Monad m) => e -> EitherT e m r
quit = left
你这样使用它:
import Pipes
import qualified System.IO as IO
stdin :: () -> Producer String IO ()
stdin () = loop $ do
eof <- lift $ lift $ IO.hIsEOF IO.stdin
if eof
then quit ()
else do
str <- lift $ lift getLine
lift $ respond str
请参阅this blog post我在哪里解释这种技术。
我在教程中不使用它的唯一原因是我认为它不太适合初学者。
答案 1 :(得分:9)
看起来像whileM_
的作业:
stdin () = whileM_ (lift . fmap not $ IO.hIsEOF IO.stdin) (lift getLine >>= respond)
或者,使用与原始示例类似的do-notation:
stdin () =
whileM_ (lift . fmap not $ IO.hIsEOF IO.stdin) $ do
str <- lift getLine
respond str
monad-loops
包还提供whileM
,它返回一个中间结果列表,而不是忽略重复操作的结果,以及其他有用的组合器。
答案 2 :(得分:1)
由于没有隐式流,所以没有像“break”这样的东西。此外,您的样本已经是小块,将用于更复杂的代码。
如果你想停止“生成字符串”,你的抽象应该支持它。即使用Consumer
中的特殊monad和/或与此相关的其他monad中的“管道”的一些“管理”。
答案 3 :(得分:0)
您只需导入System.Exit ,然后使用 exitWith ExitSuccess
EG。 if(输入=='q') 然后退出ExitSuccess 否则打印5(任何东西)