Cautions on the use

The built-in foreach and the list comprehension notation are powerful means for describing repetition. When a program is compiled, calls to foreach are converted into calls to internally-generated tail-recursive predicates, and list comprehensions are converted into calls to foreach with accumulators. Therefore, loop constructs incur almost no performance penalty when compared with recursion. Nevertheless, in order to avoid unanticipated behavior, users must take the following cautions in using them.

Firstly, iterators are matching-based. Iterators cannot change a collection, unless the goal of the loop has that effect. For example,

   ?-foreach(f(a,X) in [c,f(a,b),f(Y,Z)],write(X)).
displays b. The elements c and f(Y,Z) are skipped because they do not match the pattern f(a,X).

Secondly, variables are assumed to be global to all of the iterations, unless they are declared local, or unless they occur in the patterns of the iterators. Sometimes, one may use anonymous variables '_' in looping goals, and wrongly believe that they are local. The parser issues a warning when it encounters a variable that is not declared local but occurs alone in a looping goal.

Thirdly, no meta-terms should be included in iterators or in list constructors! For example,

   ?-D=1..5, foreach(X in D, write(X)).
is bad, since D is a meta-term. As another example,
   ?-C=(X : I in 1..5), L @=[C].
is bad since C is a meta-term. When meta-terms are included in iterators or in list constructors, the compiler may generate code that has different behavior as interpreted.

Neng-Fa Zhou 2013-01-25