Here is a way that requires no IO, instead relying on laziness and your programmer guess about which "side" of the condition happens more often. Just to have something to play with, here's a slightly slow function that checks if a number is a multiple of 10. The details of this function aren't important, feel free to skip it if anything doesn't make sense. I'm also going to turn on timing reporting; you'll see why later.
> isSpecial :: Int -> Bool; isSpecial n = last [1..10000000] `seq` (n `mod` 10 == 0)
> :set +s
(Add one 0
every five years.)
Now the idea will be this: instead of your list comprehension, we'll use partition
to split the list into two chunks, the elements that match the predicate and the ones that don't. We'll print the one of those that has more elements, so we can keep an eye on progress; by the time it's fully printed, the other one will be fully evaluated and we can inspect it however we like.
> :m + Data.List
> (matches, nonMatches) = partition isSpecial [1..20]
(0.00 secs, 0 bytes)
> nonMatches
[1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19]
(12.40 secs, 14,400,099,848 bytes)
Obviously I can't portray this over StackOverflow, but when I did the above thing, the numbers in the nonMatches
list slowly appeared on my terminal one-by-one, giving a pretty good indicator of where in the list it was currently thinking. And now, when you print matches
, the full list is available more or less instantly, as you can see by the timing report (i.e. not another 12-second wait):
> matches
[10,20]
(0.01 secs, 64,112 bytes)
But beware!
It's important that matches
and nonMatches
have types which are not typeclass polymorphic (i.e. don't have types that start with Num a => ...
or some other constraint). In the above example, I achieved this by making isSpecial
monomorphic, which forces matches
and nonMatches
to be, too, but if your isSpecial
is polymorphic, you should give a type signature for matches
or nonMatches
to prevent this problem.
Doing it this way will cause the entire nonMatches
and matches
lists to be realized in memory. This could be expensive if the original list being partitioned is very long. (But up to, say, a couple hundred thousand Int
s is not particularly long for modern computers.)