Did you know ... | Search Documentation: |
![]() | Pack fluents -- prolog/fluents.pl |
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.
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_get/3
will
raise an exception.