Applications that need to manage a state can do so by passing the state around in an additional argument, storing it in a global variable or update it in the dynamic database using assertz/1 and retract/1. Both using an additional argument and a global variable (see b_setval/2), make the state subject to backtracking. This may or may not be desirable. If having a state is that subject to backtracking is required, using an additional argument or backtrackable global variable is the right approach. Otherwise, non-backtrackable global variables (nb_setval/2) and dynamic database come into the picture, where global variables are always local to a thread and the dynamic database may or may not be shared between threads (see thread_local/1).
Engines bring an alternative that packages a state inside the engine where it is typically represented in a (threaded) Prolog variable. The state may be updated, while controlled backtracking to a previous state belongs to the possibilities. It can be accessed and updated by anyone with access to the engines’handle. Using an engine to represent state has the following advantages:
The example below implements a shared priority heap based on library
library(heaps). The predicate update_heap/1 shows the
typical update loop for maintaining state inside an engine: fetch a
command, update the state, yield with the reply and call the updater
recursively. The update step is guarded against failure. For robustness
one may also guard it against exceptions using catch/3.
Note that heap_get/3 passes the Priority and Key
it wishes to delete from the heap such that if the unification fails,
the heap remains unchanged.
The resulting heap is a global object with either a named or anonymous handle that evolves independently from the Prolog thread(s) that access it. If the heap is anonymous, it is subject to (atom) garbage collection.
:- use_module(library(heaps)).
create_heap(E) :-
empty_heap(H),
engine_create(_, update_heap(H), E).
update_heap(H) :-
engine_fetch(Command),
( update_heap(Command, Reply, H, H1)
-> true
; H1 = H,
Reply = false
),
engine_yield(Reply),
update_heap(H1).
update_heap(add(Priority, Key), true, H0, H) :-
add_to_heap(H0, Priority, Key, H).
update_heap(get(Priority, Key), Priority-Key, H0, H) :-
get_from_heap(H0, Priority, Key, H).
heap_add(Priority, Key, E) :-
engine_post(E, add(Priority, Key), true).
heap_get(Priority, Key, E) :-
engine_post(E, get(Priority, Key), Priority-Key).