Did you know ... Search Documentation:
Pack fluents -- prolog/fluents.pl
PublicShow source

This module lets you turn any goal into a Fluent (Tarau 2002). A Fluent, as defined here, is a stateful object that encapsulates a Prolog goal as it is interpreted. The solutions generated by the encapsulated goal on backtracking can be accessed by the caller without backtracking.

This is useful e.g. for iterating over solutions while building up a data structure (which would be destroyed by backtracking) or to solve several goals in parallel while keeping the solutions in sync (e.g. “zipping”), while avoiding error-prone and/or inefficient tricks like non-backtrackable assignment or materializing lists of solutions.

This implementation achieves backtracking-free access by delegating the backtracking to a separate Prolog thread that sends copies of solutions back to the calling thread via message queues, on demand. This technique was described by Samer Abdallah on the SWI-Prolog mailing list. The implementation was also inspired by Michael Hendricks’ lazy_findall implementation.

Example usage:

?- fluent_create(X, member(X, [1, 2, 3]), Fluent).
Fluent = fluent(2, <message_queue>(0x94613b8)).

?- fluent_get($Fluent, X, Exit). % Exit = nondet if further solutions may exist
X = 1,
Exit = nondet.

?- fluent_get($Fluent, X, Exit).
X = 2,
Exit = nondet.

?- fluent_get($Fluent, X, Exit).
X = 3,
Exit = det.

?- fluent_get($Fluent, X, Exit). % Fails after solutions are exhausted
false.

?- fluent_get($Fluent, X, Exit). % Just keeps failing
false.

?- fluent_destroy($Fluent). % Don't forget to release resources
true.

?- fluent_create(X, X is 3 / 0, Fluent).
Fluent = fluent(3, <message_queue>(0x9465640)).

?- fluent_get($Fluent, X, Exit). % Exceptions are re-raised by fluent_get/3
ERROR: //2: Arithmetic: evaluation error: `zero_divisor'
?- fluent_destroy($Fluent).
true.

?- fluent_create(X, (member(X, [1, 2]), write(X), nl), Fluent).
Fluent = fluent(4, <message_queue>(0x9415880)).

?- fluent_get($Fluent, X, Exit). % No side effects before fluent_get/3 call
1
X = 1,
Exit = nondet.

?- fluent_get($Fluent, X, Exit).
2
X = 2,
Exit = det.

?- fluent_destroy($Fluent).
true.
author
- Kilian Evang
license
- MIT
 fluent_create(+Template, :Goal, -Fluent) is det
Creates a fluent that will call Goal and send instantiations of Template as solutions. Goal is not yet called at this point. Fluent is a term that should be treated as an opaque reference, it may change in future versions.
 fluent_get(+Fluent, -Solution, -Exit) is semidet
Attempts to get the next solution from the fluent.

If the goal in the fluent exits, Solution is unified with a copy of the corresponding instantiation of the template given when the fluent was created, and Exit is unified with det or nondet depending on whether the goal exited without or with open choicepoints.

If the goal in the fluent raises an exception while trying to get the next solution, fluent_get/3 re-raises it.

After all solutions are exhausted, all subsequent calls to fluent_get/3 on this fluent fail (until it is destroyed).

 fluent_destroy(+Fluent) is det
Destroys the fluent and frees its resources. This should always be done when the fluent is no longer needed, regardless of whether it has further solutions or not. Subsequent calls to fluent_get/3 will raise an exception.