How do I break out of a loop in Haskell?

牧云@^-^@ 提交于 2019-12-21 11:03:37

问题


The current version of the Pipes tutorial, uses the following two functions in one of the example:

 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

As is mentinoed in the tutorial itself, P.stdin is a bit more complicated due to the need to check for the end of input.

Are there any nice ways to rewrite P.stdin to not need a manual tail recursive loop and use higher order control flow combinators like P.stdout does? In an imperative language I would use a structured while loop or a break statement to do the same thing:

while(not IO.isEOF(IO.stdin) ){
    str <- getLine()
    respond(str)
}

forever(){
    if(IO.isEOF(IO.stdin) ){ break }
    str <- getLine()
    respond(str)
}

回答1:


Looks like a job for whileM_:

stdin () = whileM_ (lift . fmap not $ IO.hIsEOF IO.stdin) (lift getLine >>= respond)

or, using do-notation similarly to the original example:

stdin () =
    whileM_ (lift . fmap not $ IO.hIsEOF IO.stdin) $ do
        str <- lift getLine
        respond str

The monad-loops package offers also whileM which returns a list of intermediate results instead of ignoring the results of the repeated action, and other useful combinators.




回答2:


I prefer the following:

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

You use it like this:

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

See this blog post where I explain this technique.

The only reason I don't use that in the tutorial is that I consider it less beginner-friendly.




回答3:


Since there is no implicit flow there is no such thing like "break". Moreover your sample already is small block which will be used in more complicated code.

If you want to stop "producing strings" it should be supported by your abstraction. I.e. some "managment" of "pipes" using special monad in Consumer and/or other monads that related with this one.




回答4:


You can simply import System.Exit, and use exitWith ExitSuccess

Eg. if (input == 'q') then exitWith ExitSuccess else print 5 (anything)



来源:https://stackoverflow.com/questions/17264360/how-do-i-break-out-of-a-loop-in-haskell

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!