4.12.9 Blackboard Primitives

The predicates described in this section store arbitrary terms in a per-module repository known as the “blackboard”. The main purpose of the blackboard was initially to provide a means for communication between branches executing in parallel, but the blackboard works equally well during sequential execution. The blackboard implements a mapping from keys to values. Keys are restricted to being atoms or small integers, whereas values are arbitrary terms. In contrast to the predicates described in the previous sections, a given key can map to at most a single term.

Each Prolog module maintains its own blackboard, so as to avoid name clashes if different modules happen to use the same keys. The “key” arguments of these predicates are subject to module name expansion, so the module name does not have to be explicitly given unless multiple Prolog modules are supposed to share a single blackboard.

The predicates below implement atomic blackboard actions.

bb_put(:Key, +Term)

A copy of Term is stored under Key. See mpg-ref-bb_put.

bb_get(:Key, ?Term)

If a term is currently stored under Key, a copy of it is unified with Term. Otherwise, bb_get/2 silently fails. See mpg-ref-bb_get.

bb_delete(:Key, ?Term)

If a term is currently stored under Key, the term is deleted, and a copy of it is unified with Term. Otherwise, bb_delete/2 silently fails. See mpg-ref-bb_delete.

bb_update(:Key, ?OldTerm, ?NewTerm)

If a term is currently stored under Key and unifies with OldTerm, the term is replaced by a copy of NewTerm. Otherwise, bb_update/3 silently fails. This predicate provides an atomic swap operation. See mpg-ref-bb_update.

Please note: If the term being stored contains attributed variables (see lib-atts) or suspended goals (see ref-sem-sec), those attributes are not stored. To retain the attributes, you can use copy_term/3 (see ref-lte-cpt).

The following example illustrates how these primitives may be used to implement a “maxof” predicate that finds the maximum value computed by some nondeterminate goal. We use a single key max6. We assume that Goal does not produce any “false” solutions that would be eliminated by cuts in a sequential execution. Thus, Goal may need to include redundant checks to ensure that its solutions are valid, as discussed above.

maxof(Value, Goal, _) :-
        bb_put(max, -1),                % initialize max-so-far
maxof(_, _, Max) :-
        bb_delete(max, Max),
        Max > 1.

        bb_get(max, Old),
        compare(C, Old, New),
        update_max(C, Old, New).

update_max(<, Old, New) :- bb_update(max, Old, New).
update_max(=, _, _).
update_max(>, _, _).



This is not necessarily a good example of using the blackboard. For instance, the implementation is not reentrant, e.g. it will not work if the Goal itself uses maxof/3. A reentrant implementation would need to ensure that multiple nested calls to maxof/3 do not interfer with each other.

Send feedback on this subject.