To understand handling of attributed variables better, I have rewritten the example of the reference manual from section 8.1 and added test cases.
This is an application of attributed variables whereby the variable carries a list (actually fulfilling the constraints of an ordset) of allowed values (which are atoms). On unification, the triggered hook procedure then either:
- intersects the ordset attributes of two variables; or
- unifies the post-unification variable with the single allowed value if the intersection yields an ordset of size 1; or
- vetoes the unification.
This generally works, and I actually found a slight hiccup in the original version whereby the hook procedure is triggered from inside the hook procedure itself needlessly when the unification for the case of “there is a single allowed value” is performed.
However, I don’t get the following test case to work, which is actually just about assigning an attribute to a variable (breaking this into two lines because Discourse insist on using a narrow designer lane of text with 20 cm of white on each side of it):
test("assign attribute with single allowed
value to unbound variable", true(X == quux)) :-
enum_domain(X,[quux]).
So, assigning an attribute listing only quux
as allowed value to X
should force X
to be quux
. Makes sense.
This doesn’t happen.
The rewritten code is:
enum_domain(X, ED) :-
nonvar(ED), % ED bound? Then set the attribute value of the hole denoted by X
!, % Unnecessary cut (last clause!) but let's be clear about the commit
list_to_ord_set(ED, EDOS), % Sort/uniquify ED to form EDOS
assertion(maplist(atom,ED)), % All should be atom
attr_key(Key), % Retrieve Key value, which *must* be the name of this module
put_attr(Y, Key, EDOS), % Instead of replacing an existing attribute on X, set it on freshvar Y...
% merge the attributes on X and Y via
% a triggered call to attr_unify_hook(Key, Y)
X = Y. % *** THIS WON'T TRIGGER THE HOOK BUT SHOULD ***
(Btw, is there a way to get the name of the current module? module/1 just sets the current module)
The original code is more direct but essentially the same:
domain(X, List) :-
list_to_ord_set(List, Domain),
put_attr(Y, domain, Domain),
X = Y.
What doesn’t happen is that X = Y
triggers the hook procedure. However, it should. The original code is clearly based on that idea. But I don’t see what could block the calling. Unless it is newly not allowed to have the hook triggered from inside the module to which it belongs?
Here are the code and test cases: