4.3.5 Term and Goal Expansion

During loading of source code, all terms being read in are subject to term expansion. Grammar rules is a special, built-in case of this mechanism. By defining the hook predicates user:term_expansion/6 and goal_expansion/5, you can specify any desired transformation to be done as clauses are loaded.

Term expansions are added by defining clauses for the following hook predicate. Such clauses should follow the pattern:

:- multifile user:term_expansion/6.
user:term_expansion(Term1, Layout1, Ids, Term2, Layout2, [token|Ids]) :- ...
        nonmember(token, Ids),
        token_expansion(Term1, Layout1, Term2, Layout2), !.

where token_expansion/4 should be a predicate defining how to transform a given Term1 into Term2. The hook is called for every Term1 read, including at end of file, represented as the term end_of_file. If it succeeds, Term2 is used for further processing; otherwise, the default grammar rule expansion is attempted. It is often useful to let a term expand to a list of directives and clauses, which will then be processed sequentially.

A key idea here is Ids, which is used to look up what expansions have already been applied. The argument is supposed to be a list of tokens, each token uniquely identifying an expansion. The tokens are arbitrary atoms, and are simply added to the input list, before expansions recursively are applied. This token list is used to avoid cyclic expansions.

The other arguments are for supporting source-linked debugging; see the reference page for details. See mpg-ref-term_expansion.

Please note: term expansions are global, i.e. they affect all code that are compiled or consulted. In particular a term expansion is not affected by module imports. Care should be taken so that a term expansion does not unintentionally affect some unrelated source code. goal_expansion/5 provides a more robust, and module aware, way to transform individual goals.

Goal expansions are added by defining the hook predicate:

M:goal_expansion(Goal1, Layout1, Module, Goal2, Layout2) :- ...

which should define how to transform a given Goal1 into Goal2. Expansions are per module and should be defined in the module M in which Goal1 is locally defined. It is called for every goal occurring in a clause being loaded, asserted, or meta-called. If it succeeds, Goal2 is used for further processing, and may be arbitrarily complex.

Please note: In general, the goal expansion can happen both at compile time and at runtime (and sometimes both, even for the same goal). For this reason the code that implements goal expansion should be present both at compile time and at runtime.

The other arguments are for supporting source-linked debugging and passing the source module; see the reference page for details.

To invoke term expansion from a program, use:

?- expand_term(Term1, Term2).

which transforms Term1 into Term2 using the built-in (for grammar rules) as well as user-defined term expansion rules. See mpg-ref-goal_expansion.

Send feedback on this subject.