Attributed variables provide a technique for extending the Prolog unification algorithm [holzbaur:1992] by hooking the binding of attributed variables. There is little consensus in the Prolog community on the exact definition and interface to attributed variables. The SWI-Prolog interface is identical to the one realised by Bart Demoen for hProlog [Demoen:CW350].
Binding an attributed variable schedules a goal to be executed at the first possible opportunity. In the current implementation the hooks are executed immediately after a successful unification of the clause-head or successful completion of a foreign language (builtin) predicate. Each attribute is associated to a module and the hook (attr_unify_hook/2) is executed in this module. The example below realises a very simple and incomplete finite domain reasoner.
:- module(domain, [ domain/2 % Var, ?Domain ]). :- use_module(library(oset)). domain(X, Dom) :- var(Dom), !, get_attr(X, domain, Dom). domain(X, List) :- sort(List, Domain), put_attr(Y, domain, Domain), X = Y. % An attributed variable with attribute value Domain has been % assigned the value Y attr_unify_hook(Domain, Y) :- ( get_attr(Y, domain, Dom2) -> oset_int(Domain, Dom2, NewDomain), ( NewDomain == [] -> fail ; NewDomain = [Value] -> Y = Value ; put_attr(Y, domain, NewDomain) ) ; var(Y) -> put_attr( Y, domain, Domain ) ; memberchk(Y, Domain) ). |
Before explaining the code we give some example queries:
?- domain(X, [a,b]), X = c | no |
?- domain(X, [a,b]), domain(X, [a,c]). | X = a |
?- domain(X, [a,b,c]), domain(X, [a,c]). | X = _G492 att(domain, [a, c], []) |
The predicate domain/2 fetches (first clause) or assigns (second clause) the variable a domain, a set of values it can be unified with. In the second clause first associates the domain with a fresh variable and then unifies X to this variable to deal with the possibility that X already has a domain. The predicate attr_unify_hook/2 is hook called after a variable with a domain is assigned a value. In the simple case where the variable is bound to a concrete value we simply check this value is in the domain. Otherwise we take the intersection of the domains and either fail if the intersection is empty (first example), simply assign the value if there is only one value in the intersection (second example) or assign the intersection as the new domain of the variable (third example).
attributes(portray)
is in effect. If the hook succeeds the
attribute is considered printed. Otherwise Module = ...
is
printed to indicate the existence of a variable.
Normal user code should deal with put_attr/3, get_attr/3 and del_attr/2. The routines in this section fetch or set the entire attribute list of a variables. Use of these predicates is anticipated to be restricted to printing and other special purpose operations.
att(Module, Value, MoreAttributes)
, where MoreAttributes
is
for the last attribute.