Skip to content

Attributed variables: Interface convergence #14

@triska

Description

@triska

There are two major deficits of the current attributed variable interface predicate attr_unify_hook/2:

  1. It is called only after the unification has already taken place.
  2. In the case of simultaneous unifications like [X,Y] = [a,b], it is called with all bindings already in place.

However, there are cases where we need to reason about variables before the unification takes place or, equivalently, any pre-existing triggering unifications are undone before the hook is called. See below for the justification.

I recommend convergence with the interface used by SICStus Prolog, also to increase portability across different Prolog systems, or the design of an interface that is at least as general.

Specifically, it would help tremendously if verify_attributes/3 were called upon unifications with the same semantics as in SICStus Prolog:

  1. first, the unifications are undone
  2. verify_attributes(+Variable, +Value, -Goals) is called
  3. the unifications are redone
  4. Goals are executed

Real-world examples

I give two examples to explain why attr_unify_hook/2 is insufficient. The first example is shown in git commit 988ea20b7. It shows that the current interface is extremely error prone to use, because it can unexpectedly bind several variables at once and then trigger the hook with an arbitrary number of bindings suddenly in place. Realistically, nobody can exhaustively think of all cases that can simultaneously happen and cover them. The author of dif/2 also ran into this exact problem, as evidenced by a filed issue about dif/2.

The second example arises in a CLP(B) implementation that uses Zero-suppressed Binary Decision Diagrams (ZDDs). A ZDD is interpreted like any decision diagram, but with the following twist: Variables that do not occur in a branch must be 0. Here is an example showing the ZDD for X=\=Y, with the dotted lines indicating 0:

ZDD for X == Y

When working with ZDDs, we must keep track of all CLP(B) variables that appear in constraints, because we need to figure out "all variables that do not occur in the ZDD".

If unifications are performed step by step, i.e., just as with individual unifications, no problems arise, even with attr_unify_hook/2. For example, suppose we have X = 1, Y = 1. The first unification leads us into the right branch of the ZDD. We arrive at true, which means that this is true if all variables that do not occur in the ZDD (i.e., Y), are 0. Therefore, we post Y=0 from within attr_unify_hook/2. The second unification will therefore automatically fail.

But suppose the unifications are performed simultaneously, as in [X,Y] = [1,1]. The first unification again leads us into the right branch of the ZDD, but now we cannot find out which variables do not occur in the ZDD, because Y is no longer a variable at the time the hook is called!

More information about this issue: Minato task.

There may be ways to work around this, but they involve essentially duplicating the Prolog view of variables within the program, using IDs of variables to distinguish them. Also, this is not at all how we expect to reason about our datastructures.

For these reasons, it is very desirable that the unification hooks are called before the bindings are in place, and for each binding individually.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions