Magnus Kronqvist
Magnus Kronqvist

Reputation: 1589

Failure to produce a lazy stream

I will try to state this question as short and to the point as possible.

I expected this computation to complete after inputting three characters to the command line. However, it does not. I am curious as to why this is.

return . take 3 =<< sequence (repeat getChar)

Edit:

I should add that I am trying to separate selection from generation and therefore wish to produce an infinite stream of characters that I select from.

Upvotes: 2

Views: 269

Answers (2)

shang
shang

Reputation: 24802

I/O sequences actions deterministically, so what you are really saying is that you want to execute the getChar action an infinite number of times and after that has finished, you want to take the three first items from the generated list. You can probably see how this will take a long time.

If you want to perform I/O lazily (and non-deterministically), you can do so using the function unsafeInterleaveIO from the System.IO.Unsafe package.

One way to implement what you want is, for example, like this:

import System.IO.Unsafe

lazyIOSequence :: [IO a] -> IO [a]
lazyIOSequence [] = return []
lazyIOSequence (x:xs) = do
    first <- unsafeInterleaveIO $ x
    rest  <- unsafeInterleaveIO $ lazyIOSequence xs
    return $ first:rest

test = return . take 3 =<< lazyIOSequence (repeat getChar)

However, the now the actual prompting of data from the user gets delayed until you evaluate elements of the list, so it might not behave as you'd like. In fact, lazy I/O streams are in general considered problematic (see e.g. the questions "Haskell lazy I/O and closing files" and "What's so bad about Lazy I/O?").

The currently recommended way to separate production from selection and processing is to use one of the various strict, stream-processing libraries such as enumerator, pipes or conduit.

Upvotes: 7

dave4420
dave4420

Reputation: 47052

  1. Effects to the right of =<< complete before effects to the left of =<< begin.
    • This means that if the action to the right of =<< would run forever then the entire action will run forever.
  2. sequence takes a list of actions and gives back an action that performs all those actions.
    • If you give sequence an infinite list of actions, it will run forever.
  3. repeat takes a value, and gives an infinite list of that value, repeated.

Do you see now why you get the behaviour you experience?


This would work, because there is no infinite list:

sequence (take 3 $ repeat getChar)

But presumably your real selection function is more complicated than "take the first three". What is it?

Upvotes: 6

Related Questions