0

This is an example from Learn You a Haskell:

main = do
    putStrLn "hello, what's your name?"
    name <- getLine
    putStrLn ("Hey, " ++ name ++ ", you rock!")

The same redone without do for clarity:

main =
    putStrLn "hello, what's your name?" >>
    getLine >>= \name ->
    putStrLn $ "Hey, " ++ name ++ ", you rock!"

How am I supposed to loop it cleanly (until "q"), the Haskell way (use of do discouraged)?

I borrowed this from Haskell - loop over user input

main = mapM_ process . takeWhile (/= "q") . lines =<< getLine
    where process line = do
                            putStrLn line

for starters, but it won't loop.

5
  • 1
    The use of do isn't discouraged. There's some discussion about whether or not to use do notation when introducing monads to beginners because it might give them even crazier ideas about what monads are, but in normal haskell use of do is fine. Also, if you know how to do it with do, you know how to do it without. Your problem here has nothing to do with do notation. Commented Nov 1, 2017 at 5:26
  • Also, your last code segment you have there doesn't work because you wrote getLine instead of getContents Commented Nov 1, 2017 at 5:30
  • Here's a library which defines a large number of looping combinators. Commented Nov 1, 2017 at 5:49
  • 2 Cubic: discouraged for me only :) . To stay away from crazy ideas precisely. Commented Nov 1, 2017 at 6:32
  • 2 Cubic: getContents helps. Unfortunately, I'm still at a loss about how to insert prompt/response into the picture. Commented Nov 1, 2017 at 6:51

2 Answers 2

1

You can call main again and check if your string is "q" or not.

import Control.Monad

main :: IO ()
main =
    putStrLn "hello, what's your name?" >>
    getLine >>= \name ->
                  when (name /= "q") $ (putStrLn $ "Hey, " ++ name ++ ", you rock!") >> main

λ> main
hello, what's your name?
Mukesh Tiwari
Hey, Mukesh Tiwari, you rock!
hello, what's your name?
Alexey Orlov
Hey, Alexey Orlov, you rock!
hello, what's your name?
q
λ> 
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks! This is the exact answer. Is there a way to employ if instead of when, BTW?
About if: not here, I see. when exists precisely for this kind of job.
@AlexeyOrlov No, if works perfectly fine. IO actions are just values you can shuffle around: main = _ >>= \name -> if name /= "q" then main else return (). That's how when is implemented: when cond act = if cond then act else pure () (pure = return for IO (and Monads in general)).
Thanks! It makes one think... Why the trouble of defining when if is does not make your life any easier :) ?
0

May be you can also use laziness on IO type by adapting the System.IO.Lazy package. It basically includes only run :: T a -> IO a and interleave :: IO a -> T a functions to convert IO actions into lazy ones back and forth.

import qualified System.IO.Lazy as LIO

getLineUntil :: String -> IO [String]
getLineUntil s = LIO.run ((sequence . repeat $ LIO.interleave getLine) >>= return . takeWhile (/=s))

printData :: IO [String] -> IO ()
printData d = d >>= print . sum . map (read :: String -> Int)

*Main> printData $ getLineUntil "q"
1
2
3
4
5
6
7
8
9
q
45

In the above code we construct an infinite list of lazy getLines by repeat $ LIO.interleave getLine of type [T String] and by sequence we turn it into T [String] type and proceed reading up until "q" is received. The printData utility function is summing up and printing the entered integers.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.