The article goes into the deep details, and is borderline over my head still, but the key notion is quite delicious. The problem it is addressing is that reading from a file is necessarily a bit messy, since you have to deal with problems like EOF; this makes it hard to hide the fact that you're dealing with a file, and particularly makes functional composition hard. You can kind of handwave away those problems, but generally at the price of losing the consumer's ability to lazily consume only as much of the file as it wants and then stop. So the question is, how do you build something that is (a) generalizable; (b) composable; (c) lazily driven by the consumer; and (d) allows the producer to say when producer-side events happen.
The result is a very elegant decomposition of concerns. Basically, the Iteratee pattern is sort of like the usual Iterator, but much more sophisticated: instead of having a single iteration engine that is pulling from the sequence (as Iterator usually does), there are essentially two engines, one on each side, with clear contracts between them. The producer engine exposes three states -- Empty, meaning that there's nothing more available now; EOF, meaning that the sequence is really finished; and El(x), meaning that x is the next element in the sequence. The consumer engine exposes two states: Done, which signifies that we're finished consuming and have a final result value; and Cont, which means that I have an intermediate value, and want to keep consuming further entries.
Putting this together, you can implement the engines in isolation. The linked article shows producer engines for List and File -- each mainly thinks in terms of the current state of the consumer engine when deciding whether to keep on producing more elements, but also knows how to handle end-of-list or end-of-file conditions as appropriate. And it shows a bunch of consumer functions, each of which works with a producer engine to do things like count the number of elements, drop some from the front, choose alternate items, and so on.
It looks like a lot of work to do fairly simple stuff, but the high concept here is compelling: basically, since this design abstracts *both* sides of the equation, you can now mix-and-match them much more arbitrarily than usual. The payoff, at the end of the article, is the enumFile() function, which guarantees proper file behaviour, acts purely lazily, and lets you apply and composite arbitrary consumers of the file contents. Very sweet...