12.2 Using the macros
The following declarations and built-ins control macro expansion:
Macros are selectively applied only to terms of the specified class.
TermClass can take two forms:
local macro(+TermClass, +TransPred, +Options)
define a macro for the given TermClass. The transformation will
be performed by the predicate TransPred.
- export macro(+TermClass, +TransPred, +Options)
as above, but available to other modules.
- erase_macro(+TermClass, +Options)
erase a currently defined macro for TermClass. This can only be done
in the module where the definition was made.
- current_macro(?TermClass, ?TransPred, ?Options, ?Module)
retrieve information about currently defined visible macros.
The +TransPred argument specifies the predicate that will perform the
transformation. It has to be of arity 2 or 3 and should have the form:
- transform all terms with the specified functor
- transform all terms of the specified type, where Type
is one of compound, string, integer, rational, float, breal, atom,
At transformation time, the system will call TransPred in the module
where macro/3 was invoked.
The term to transform is passed as the first argument, the second is a free
variable which the transformation predicate should bind to the
transformed term, and the optional
third argument is the module where the term is read or written.
trans_function(OldTerm, NewTerm [, Module]) :- ... .
Options is a list which may be empty (in this case the macro defaults
to a local read term macro) or contain specifications from
the following categories:
The following shorthands exist:
- This is a read macro and shall be applied after reading a
- This is a write macro and shall be applied before printing
- Transform all terms (default).
- Transform only if the term is a program clause,
i.e. inside compile/1, assert/1 etc.
Write macros are applied using the 'C' option in the printf/2 predicate.
- Goal-read-macros are transformed only if the term is a
subgoal in the body of a program clause.
Goal-write macros are applied using the 'G' option in the
- additional specification
- Disable transformation of subterms (optional).
- Consider only the whole term, not subterms (optional).
Here is an example of a conditional read macro:
local/export portray(+TermClass, +TransPred, +Options)
but the write-option is implied.
- inline(+PredSpec, +TransPred)
is the same as a goal-read-macro. The visibility is inherited
from the transformed predicate.
If the transformation function fails, the term is not transformed. Thus,
a(1, zzz) is transformed into b(zzz) but a(-1, zzz)
is not transformed.
The arguments are transformed bottom-up. It is possible to protect the
subterms of a transformed term by specifying the flag protect_arg.
[eclipse 1]: [user].
trans_a(a(X,Y), b(Y)) :- % transform a/2 into b/1,
number(X), % but only under these
X > 0. % conditions
:- local macro(a/2, trans_a/2, ).
user compiled traceable 204 bytes in 0.00 seconds
[eclipse 2]: read(X).
X = b(hello) % transformed
[eclipse 3]: read(X).
X = a(-1, bye) % not transformed
A term can be protected against transformation by quoting it with
the “protecting functor” (by default it is no_macro_expansion/1):
Note that the protecting functor is itself defined as a macro:
[eclipse 4]: read(X).
a(1, no_macro_expansion(a(1, zzz))).
X = b(a(1, zzz)).
A local macro is only visible in the module where it has been defined.
When it is defined as exported, then it is copied to all
other modules that contain a
for this module.
The transformation function should also be exported in this case.
There are a few global macros predefined by the system, e.g. for
-->/2 (grammar rules, see below) or with/2 and of/2
(structure syntax, see section 5.1).
These predefined macros can be hidden by local macro definitions.
:- export macro(no_macro_expansion/1, trprotect/2, [protect_arg]).
The global flag macro_expansion can be used to disable
macro expansion globally, e.g. for debugging purposes.
Use set_flag(macro_expansion, off) to do so.
The next example shows the use of a type macro. Suppose we want to represent
integers as s/1 terms:
When we want to convert the s/1 terms back to normal integers so that they
are printed in the familiar form, we can use a write macro.
Note that we first erase the read macro for integers, otherwise we would get
unexpected effects since all integers occurring in the definition of
tr_s/2 would turn into s/1 structures:
[eclipse 1]: [user].
tr_int(N, s(S)) :- N > 0, N1 is N-1, tr_int(N1, S).
:- local macro(type(integer), tr_int/2, ).
[eclipse 2]: read(X).
X = s(s(s(0)))
[eclipse 3]: erase_macro(type(integer)).
[eclipse 4]: [user].
tr_s(s(S), N) :- tr_s(S, N1), N is N1+1.
:- local macro(s/1, tr_s/2, [write]).
[eclipse 2]: write(s(s(s(0)))).