Since Haskell is lazy you can feed final list to its construction (like the prime sieve examples).
Combination in a single-pass is optimisable by loop-fusion. Though optimisations are famous for 'flaking out' at the worst moment.
Meanwhile, with a Collector, you can read the inputs with standard IO actions just fine, and feed them as they are produced.