Closures in Prolog

% This predicate int_seq/1 returns another predicate, which
% we define anonymously in the body of int_seq/1. The
% returned predicate closes over the variable I to form a closure.
int_seq(NextInt) :-
    NextInt = int_seq(0).

int_seq(I, Next, NewI) :-
    NewI is I + 1,
    Next = NewI.

% We call int_seq/1, assigning the result (a predicate) to NextInt.
% This predicate captures its own I value, which will be updated
% each time we call NextInt.
main :-
    int_seq(NextInt),

    % See the effect of the closure by calling NextInt a few times.
    call(NextInt, N1),
    writeln(N1),
    call(NextInt, N2),
    writeln(N2),
    call(NextInt, N3),
    writeln(N3),

    % To confirm that the state is unique to that particular
    % predicate, create and test a new one.
    int_seq(NewInts),
    call(NewInts, N4),
    writeln(N4).

In Prolog, we can simulate closures using predicates that carry state. Here’s how the code works:

  1. The int_seq/1 predicate returns another predicate (int_seq/3) that acts as our closure. This predicate keeps track of the current state (the value of I).

  2. int_seq/3 takes three arguments: the current state I, the next value Next, and the new state NewI. It increments the state and unifies Next with the new value.

  3. In the main predicate, we create our closure by calling int_seq(NextInt). NextInt is now bound to a predicate that we can call to get the next integer in the sequence.

  4. We demonstrate the effect of the closure by calling NextInt multiple times using the call/2 predicate. Each call increments the internal state and returns the next number in the sequence.

  5. To show that each closure maintains its own state, we create a new closure NewInts and call it once, showing that it starts from the beginning of the sequence.

To run this program:

$ swipl -q -t main -f closures.pl
1
2
3
1

This example demonstrates how Prolog can simulate closures using predicates that maintain state between calls. While Prolog doesn’t have built-in support for closures in the same way as some other languages, this approach allows us to achieve similar functionality.