Reputation: 199
Haskell is challenging! What I figured out so far is that I can do the following to simulate a for-loop in Haskell to get a list of numbers from the user:
myList <- sequence [putStr "Enter an integer: " >> (
\s -> read s :: Int) <$> getLine | t <- [1..5]]
Great! So myList
contains five integers that I have entered. Great! But here's the catch. Instead of a for-loop that iterates five times (or any finite number of times) how can I convert the above to the equivalent while-loop?
So what I'm thinking of is this, but it won't work, unfortunately. Is there some "magic" way to make it work?
takeWhile (\x -> x > 0) $ sequence [putStr "Enter an integer: " >> (
\s -> read s :: Int) <$> getLine | t <- [1..]]
The problem is that (\x -> x > 0)
works with Ints. (Or any Num type.) But what's coming from that list is really a bunch of IO Ints. x > 0
returns a Bool. I need a function that returns an IO Bool? I'm a little lost. Can someone point the way to Haskell enlightenment for me?! Studying this on my own isn't exactly easy!!! Thank you so much!!!
Upvotes: 4
Views: 505
Reputation: 26161
@amalloy's answer is great. It's kind of conditional sequencing.
Perhaps we may attempt generalizing it further by inventing takeWhileM
(basically conditional sequencing) which sequences only an initial part of an indefinitely long list of actions while a predicate satisfies.
takeWhileM :: Monad m => (a -> Bool) -> [m a] -> m [a]
takeWhileM f (a:as) = a >>= \n -> if f n then (n:) <$> takeWhileM f as
else pure []
So for this particular case i run it like
λ> takeWhileM (> 0) . repeat $ putStr "Enter an integer:" >> readLn
Enter an integer:1
Enter an integer:2
Enter an integer:3
Enter an integer:4
Enter an integer:5
Enter an integer:0
[1,2,3,4,5]
Upvotes: 0
Reputation: 91907
You cannot write this program with a sequence
of an infinite list of IO actions. Any operations you perform "outside" of the sequence will be unable to inspect its contents, and any operations inside the sequence will be unable to stop it from continuing.
Instead, you must write an IO action which reads an Int, inspects it, and decides whether to continue or to stop at that time.
positiveInts :: IO [Int]
positiveInts = do
putStr "Enter an integer: "
i <- readLn
if i <= 0
then pure []
else (i:) <$> positiveInts
Upvotes: 6