A shorter way to conditionally "return ()" in the monad chain (>>, >> =) in Haskell?
I have this simple code that reads a line and prints it unlimitedly.
main :: IO () main = getLine >>= putStrLn >> main Now I want to exit after calling getLine if the string is "quit" or "exit".
My attempt:
main :: IO () main = do line <- getLine if line == "exit" || line == "quit" then return () else putStrLn line >> main Doesn't look like idiom to me. Is there a better way?
Control.Monad.unless (and this is a slightly more popular cousin, when ) abstracts this template from your code:
import Control.Monad (unless) main = do line <- getLine unless (line == "exit" || line == "quit") $ do putStrLn line main -- or when (line /= "exit" && line /= "quit") $ do putStrLn line main And conditional return () , followed by unconditional code, will not do the trick, since return is just a function, not a stream control keyword, as in most other languages.
Using pipes-4.0 :
import Pipes import qualified Pipes.Prelude as P main = runEffect $ P.stdinLn >-> P.takeWhile (`notElem` ["quit", "exit"]) >-> P.stdoutLn It seems that you are worried about the consistent perception of the code due to the use of if / else and notation. You can try something like:
main = getLine >>= proc where proc s | s == "exit" || s == "quit" = return () | otherwise = putStrLn s >> main Trying to be fashionable:
module Main where import Control.Monad import Control.Monad.Trans.Maybe import Control.Monad.Trans.Class import System.IO isValid s = s โ "quit" && s โ "exit" getL โท MaybeT IO String getL = do s โ lift getLine guard (isValid s) return s main = runMaybeT main' where main' = do lift $ putStr "Enter line: " lift $ hFlush stdout s โ getL lift $ putStrLn $ "Your line is: " โงบ s main' We can create a helper function that repeats the given action when it returns a value:
import Control.Monad import Control.Monad.Trans import Control.Monad.Trans.Maybe while :: (Monad m) => MaybeT mb -> m () while k = runMaybeT (forever k) >> return () As soon as k returns mzero , the loop stops. Then we can use it to break the loop anywhere using standard MonadPlus combinators:
main = while $ do l <- lift getLine guard $ l /= "quit" lift $ putStrLn l Or in one line:
main = while $ mfilter (/= "quit") (lift getLine) >>= lift . putStrLn Update: Perhaps the simplest solutions use whileJust_ from monad-loops:
isValid s | s /= "quit" = Just s | otherwise = Nothing main = whileJust_ (isValid `liftM` getLine) putStrLn