Attributed Variable Handlers

## 16.7 Attributed Variable Handlers

- Printing Attributed Variables

An attributed variable is a variable with some additional information which is ignored by ordinary object level system predicates. Meta level operations on attributed variables are handled by extensions which know the contents of their attributes and can specify the outcome of each operation. This mechanism is implemented using attributed variable handlers, which are user-defined predicates invoked whenever an attributed variable occurs in one of the predefined operations. The handlers are specified in the attribute declaration meta_attribute(Name, HandlerList), the second argument is a list of handlers in the form

>     [unify:UnifyHandler, test_unify:TUHandler, ...]

Handlers for operations which are not specified or those that are true/0 are ignored and never invoked. If Name is an existing extension, the specified handlers replace the current ones.
Whenever one of the specified operations detects an attributed variable, it will invoke all handlers that were declared for it and each of them receives either the whole attributed variable or its particular attribute as argument. The system does not check if the attribute that corresponds to a given handler is instantiated or not; this means that the handler must check itself if the attributed variable contains any attribute information or not. For instance, if an attributed variable X{a:_, b:_, c:f(a)} is unified with the attributed variable Y{a:_, b:_, c:f(b)}, the handlers for the attributes a and b should treat this as binding of two plain variables because their attributes were not involved. Only the handler for c has any work to do here. The library suspend.pl can be used as a template for writing attributed variable handlers.
The following operations invoke attributed variable handlers:

- unify: the usual unification. The handler procedure is

  > unify_handler(+Term, ?Attribute [,SuspAttr])

  The first argument is the term that was unified with the attributed variable, it is either a nonvariable or an attributed variable. The second argument is directly the contents of the attribute slot corresponding to the extension, i.e. it is not the whole attributed variable. When this handler is invoked, the attributed variable is already bound to Term. The optional third argument is the suspend-attribute of the former variable; it may be needed to wake the variable's 'constrained' suspension list.
  If an attributed variable is unified with a standard variable, the variable is bound to the attributed variable and no handlers are invoked. If an attributed variable is unified with another attributed variable or a non-variable, the attributed variable is bound (like a standard variable) to the other term and all handlers for the unify operation are invoked. Note that several attributed variable bindings can occur e.g. during a head unification and also during a single unification of compound terms. The handlers are only invoked at certain trigger points (usually before the next predicate call).
- test_unify: the unification which is not supposed to trigger constraints propagation, it is used e.g. in the not_unify/2 predicate. The handler procedure is

  > test_unify_handler(+Term, ?Attribute)

  where the arguments are the same as for the unify handler. During the execution of the handler the attributed variable is bound to Term, however when all local handlers succeed, all bindings are undone.
- compare_instances: computation of instance, subsumption and variance relationship, as performed by the built-ins instance/2 and variant/2. The handler procedure is

  > instance_handler(-Res, ?TermL, ?TermR)

  and its arguments are similar to the ones of the compare_instances/3 predicate. The handler is invoked with one or both of TermL and TermR being attributed variables. The task of the handler is to compare the two terms and instantiate Res to either = (if the terms are variants) or < (if TermL is a proper instance of TermR). If the terms are incomparable, the handler should fail. If TermR is proper instance of TermL, the handler should either return > or fail. All bindings made in the handler will be undone after processing the local handlers.
- copy_term: the handler is invoked when terms are copied by the copy_term/2 or copy_term_vars/3 built-ins. The handler procedure is

  > copy_handler(?AttrVar, ?Copy)

  AttrVar is the attributed variable encountered in the copied term, Copy is its corresponding variable in the copy. All extension handlers receive the same arguments. This means that if the attributed variable should be copied as an attributed variable, the handler must check if Copy is still a free variable or if it was already bound to an attributed variable by a previous handler.
- suspensions: this handler is invoked by the suspensions/2 predicate to collect all the suspension lists inside the attribute. The handler call pattern is

  > suspensions_handler(?AttrVar, -ListOfSuspLists, -Tail)

  AttrVar is an attributed variable. The handler should bind ListOfSuspLists to a list containing all the attribute's suspension lists and ending with Tail.
- delayed_goals_number: handler is invoked by the delayed_goals_number/2 predicate. The handler call pattern is

  > delayed_goals_number_handler(?AttrVar, -Number)

  AttrVar is the attributed variable encountered in the term, Number is the number of delayed goals occurring in this attribute. Its main purpose is for the first-fail selection predicates, i.e. it should return the number of constraints imposed on the variable.
- get_bounds: This handler is used by the predicate get_var_bounds/3 to retrieve information about the lower and upper bound of a numeric variable. The handler should therefore only be defined if the attribute contains that kind of information. The handler call pattern is

  > get_bounds_handler(?AttrVar, -Lwb, -Upb)

  The handler is only invoked if the variable has the corresponding (non-empty) attribute. The handler should bind Lwb and Upb to numbers (any numeric type) reflecting the attribute's information about lower and upper bound of the variable, respectively. If different attributes return different bounds information, get_var_bounds/3 will return the intersection of these bounds. This can be empty (Lwb > Upb).
- set_bounds: This handler is used by the predicate set_var_bounds/3 to distribute information about the lower and upper bound of a numeric variable to all its existing attributes. The handler should therefore only be defined if the attribute can incorporate this kind of information. The handler call pattern is

  > set_bounds_handler(?AttrVar, +Lwb, +Upb)

  The handler is only invoked if the variable has the corresponding (non-empty) attribute. Lwb and Upb are the numbers that were passed to set_var_bounds/3, and the handler is expected to update its own bounds representation accordingly.
- print: attribute printing in write/1,2, writeln/1,2, printf/2,3 when the m option is specified. The handler procedure is

  > print_handler(?AttrVar, -Attribute)

  AttrVar is the attributed variable being printed, Attribute is the term which will be printed as a value for this attribute, prefixed by the attribute name. If no handler is specified for an attribute, or the print handler fails, the attribute will not be printed.

The following handlers are still supported for compatibility, but their use is not recommened:

- pre_unify: this is another handler which can be invoked on normal unification, but it is called before the unification itself occurs. The handler procedure is

  > pre_unify_handler(?AttrVar, +Term)

  The first argument is the attributed variable to be unfied, the second argument is the term it is going to be unified with. This handler is provided only for compatibility with SICStus Prolog and its use is not recommended, because it is less efficient than the unify handler and because its semantics is somewhat unclear, there may be cases where changes inside this handler may have unexpected effects.
- delayed_goals: this handler is superseded by the suspensions-handler, which should be preferred. If there is no suspensions- handler, this handler is invoked by the obsolete delayed_goals/2 predicate. The handler procedure is

  > delayed_goals_handler(?AttrVar, ?GoalList, -GoalCont)

  AttrVar is the attributed variable encountered in the term, GoalList is an open-ended list of all delayed goals in this attribute and GoalCont is the tail of this list.

### 16.7.1 Printing Attributed Variables

The different output predicates treat attributed variables differently. The write/1 predicate prints the attributes using the print-handlers, while writeq/1 prints the whole attribute, so that the attributed variable can be read back. The printf/2 predicate has two options to be combined with the w format: M forces the whole attributed variable to be printed together with all its attributes in the standard format, so that it can be read back in. With the m option the attributed variable is printed using the handlers defined for the print operation. If there is only one handled attribute, the attributed variable is printed as

> X{Attr}

where Attr is the value obtained from the handler. If there are several handled attributes, all attributes are qualified like in

> X{a:A, b:B, c:C}.

An attributed variable `X{m:a}` with print handler =/2 can thus be printed in different ways, e.g.: 1

>     printf("%w", [X{m:a}])   or write(X{m:a}):    X
>     printf("%vMw", [X{m:a}]) or writeq(X{m:a}):   _g246{suspend : _g242, m : a}
>     printf("%mw", [X{m:a}]):                      X{a}
>     printf("%Mw", [X{m:a}]):                      X{suspend : _g251, m : a}
>     printf("%Vmw", [X{m:a}]):                     X_g252{a}

Write macros for attributed variables are not allowed because one extension alone should not decide whether the other attributes will be printed or not.