This is the exercise from https://haskell.mooc.fi/ training
-- Ex 5: define the IO operation readUntil f, which reads lines from
-- the user and returns them as a list. Reading is stopped when f
-- returns True for a line. (The value for which f returns True is not
-- returned.)
--
-- Example in GHCi:
-- *Set11> readUntil (=="STOP")
-- bananas
-- garlic
-- pakchoi
-- STOP
-- ["bananas","garlic","pakchoi"]
readUntil :: (String -> Bool) -> IO [String]
readUntil f = todo
Would you be able to provide me a hint / solution using do notation ? I am a beginning with the do notation and the "conditional logic" as well as looping is too complex for me at the moment.
Thank you so much
CodePudding user response:
With only do-notation and conditional statements I found the following solution:
readUntil :: (String -> Bool) -> IO [String]
readUntil f = do x <- getLine;
if f x then (return []) else (do k <- readUntil f
return (x : k))
The function first reads one line with getLine
and then checks whether (f x)
is true. It then returns just the empty list. We can't just write ... if f x then [] ...
because []
has not the type IO [String]
but just [String]
. To make []
the type IO [String]
we can use the function return
or pure
but with do-notation I use the return
function, because it is included in the Monad
typeclass.
If f x
equals False
we then use a second do-block to recursively call the function again and again until we get an Input for which f x == True
and therefore returns the empty list. The do-notation is necessary because k must have type [String]
, but readUntil
has the type IO [String]
. We can't use the :
("cons") operator with object of type IO String
and therefore can't generate the list we want. We then add x to the list k of all our other inputs and return it.