Where using a stateful iterator is like reading from a stream — you just issue :get calls
to read the successive elements — using a functional iterator is like walking down a list: there’s
a :first operation which is like car, and a :rest operation which is like
cdr. You have to use :rest explicitly at each step to get the next instance of the
iterator, just as you would have to use cdr to get the next cons of the list. And as with
lists, the functional iterators are persistent: calling :rest on an iterator instance doesn’t
invalidate it; you can stash it away, so you can restart the iteration at that point anytime.
FSet’s functional iterators are not quite as efficient as the stateful ones, since each :rest
operation has to cons a new instance, so they’re not used by default. But they have occasional uses;
for instance, in parsing, when you want to be able to backtrack easily. Also, they’re thread-safe.
To obtain a functional iterator, call fun-iterator on the collection. Functional iterators
are also implemented as closures, with these operations:
:first returns the element or pair addressed by this iterator instance
:rest returns the next iterator instance
:more? returns true if there are more elements remaining
:empty? returns true if there are no more elements remaining
Specifically, on a set iterator or a non-pair bag iterator, :first returns two values. If
the iterator is not exhausted (there are more elements remaining), it returns the next element and a
true second value; if it is exhausted, it returns nil and false. On a map iterator, a pair
bag iterator, or a binary relation iterator, it returns three values: if not exhausted, the domain
element, the range element or multiplicity, and true; if exhausted, two nils and false.
So there are actually three ways to detect exhaustion: :more? or :empty?, which return
booleans with opposite senses, or just call :first until the second or third value is false.
Use whichever is most convenient.
FSet provides functional iterators on these classes: set,
map, bag,
seq, replay-set, replay-map, and 2-relation. For convenience, it also provides
fun-iterator methods on these CL builtin types: list, vector, string,
and sequence.