Haskell way to do I / O loops (without explicit recursion)?

I want to read a list of strings separated by newlines from STDIN until a newline is witnessed, and I need a type action IO [String]. Here's how I would do it with recursion:

myReadList :: IO String
myReadList = go []
where 
    go :: [String] -> IO [String]   
    go l = do {
                 inp <- getLine;
                 if (inp == "") then 
                     return l;
                 else go (inp:l);
                }

However, this method of using go hides readability and is so common that, ideally, I would like to abstract it.

So this was my attempt:

whileM :: (Monad m) => (a -> Bool) -> [m a] -> m [a]
whileM p []     = return []
whileM p (x:xs) = do
    s <- x
    if p s
    then do
        l <- whileM p xs
        return (s:l)
    else
        return []

myReadList :: IO [String]
myReadList = whileM (/= "") (repeat getLine)

I assume there is some default implementation of this whileMor something similar already. However, I cannot find him.

Can someone point out what is the most natural and elegant way to solve this problem?

+4
source share
3

unfoldWhileM whileM, , ( ).

myReadList = unfoldWhileM (/= "") getLine
+12

, , Control.Monad.Loop, . , Monad Loops.

. , , Haskell Lazy, ;

(sequence . repeat $ getLine) >>= return . takeWhile (/="q")

, IO [String]. Nah... , IO actişons . System IO Lazy . 2- .

run        :: T a -> IO a
interleave :: IO a -> T a

, run Lazy IO- IO, interleave . , :

import qualified System.IO.Lazy as LIO

gls = LIO.run (sequence . repeat $ LIO.interleave getLine) >>= return . takeWhile (/="q")

Prelude> gls >>= return . sum . fmap (read :: String -> Int)
1
2
3
4
q
10
+1

, streaming:

import Streaming
import qualified Streaming.Prelude as S

main :: IO ()
main = do
    result <- S.toList_ . S.takeWhile (/="") . S.repeatM $ getLine
    print result

A solution that displays prompts, keeping them separate from reading actions:

main :: IO ()
main = do
    result <- S.toList_
            $ S.zipWith (\_ s -> s)
                        (S.repeatM $ putStrLn "Write something: ")
                        (S.takeWhile (/="") . S.repeatM $ getLine)
    print result
+1
source

Source: https://habr.com/ru/post/1688789/


All Articles