PrologEZ
Advanced · Lesson 39 of 43

Everything is a term: operators and DSLs

Surface syntax that denotes plain terms, op/3, and Prolog as the host of a small language.

Metaprogramming

Accordingly it is possible to read and reason about programs, to update or synthesise programs, and to execute programs, possibly in different conditions.

  • In Java, reflection is a way of inspecting certain parts of a program, even at run-time; other libraries allow for so-called "code instrumentation".
  • Scala has advanced techniques to manipulate and execute Scala programs (documentation).

Metaprogramming in Prolog

Very powerful, simple and direct: the clauses of a program are actually terms, even syntactically, and this paves the way for supporting virtually any metaprogramming technique. Especially, this is used in AI to tweak Prolog into a component of symbolic reasoning.

The ingredients of this and the next lessons:

  1. "everything is a term";
  2. libraries to "call" terms;
  3. dynamic theories;
  4. metainterpretation.

Everything is a term

File surface-syntax.pl. Surface syntax that actually denotes standard terms:

  1. goals have the same syntax as terms;
  2. binary infix operators, e.g. X = 1 denotes '='(X, 1). There are dozens: +, -, *, /, :-, ==, ;, ...;
  3. unary prefix operators, e.g. -X denotes '-'(X): +, -, $, ...;
  4. list syntax: [A,B,C] is '.'(A,'.'(B,'.'(C,'[]'))), and [A,B,C|T] is '.'(A,'.'(B,'.'(C,T)));
  5. comma syntax (when used without functor or predicate): (A,B,C,D) is ','(A,','(B,','(C,D))), and (A) is A;
  6. braces: {A,B,C,D} is '{}'(','(A,','(B,','(C,D)))), and {p(X)} is '{}'(p(X)).
?- G = (X = 1), G =.. L
?- T = (a + b * c), T =.. L
?- T = -X, T =.. L
?- (A, B, C) = ','(1, ','(2, 3))
?- (a) == a
?- {p(X)} = '{}'(p(X))
?- {0, 1, 2+3} = '{}'(A), A =.. L
?- [a, b|T] = '[|]'(a, '[|]'(b, T))

The goal {0,1,2+3} = '{}'(A), A =.. L, gives A = (0,1,2+3) and L = [',', 0, (1, 2+3)]: the braces wrap a comma term, whose functor is ',', with two arguments, 0 and the comma term (1, 2+3).

Operators

File operators.pl. An operator is a predicate or functor that is declared to have a different syntax, that is, infix if 2-ary or prefix if 1-ary. It can also be a symbol without the need for quotes.

It must be defined by the predicate op/3, executed before it is used:

  • the first argument is a number, describing the priority with respect to other operators (lower binds tighter);
  • the second is a literal describing the syntax: xfy, yfx, xfx for infix, fx (and fy) for prefix, where f is the operator and x, y the arguments (x: strictly lower priority, y: lower or equal);
  • the third is the operator itself.

An operator as a predicate, here approximate equality of numbers:

% use as predicate
:- op(100, xfx, '~').
N1 ~ N2 :- N1 > N2, !, Delta is N1 - N2, Delta < 0.1.
N1 ~ N2 :- N2 ~ N1.

% use as functor
:- op(100, xfy, '::').
to_list(A :: B, [A | B2]) :- to_list(B, B2).
to_list(nil, []).
?- 10 ~ 10.01
?- to_list(10 :: 20 :: 30 :: nil, L)
?- X = (10 :: 20 :: nil), X =.. L
  • ~ is used as a predicate: 10 ~ 10.01 is a goal.
  • :: is used as a functor: 10 :: 20 :: 30 :: nil is a term, a list-like structure, and because :: is xfy it nests to the right, exactly like the list constructor.

Prolog as host for a DSL

File dsl.pl. Prefix operators let Prolog host a small domain-specific language for describing a person:

% use as DSL
:- op(100, fx, person).
:- op(100, fx, name).
:- op(100, fx, age).
:- op(100, fx, nationality).
:- op(100, fx, married).

person {
    name 'Rossi Marco',
    age 30,
    nationality italy,
    married false
}.
?- person { name NM, age A, nationality N, married M }

The fact is an ordinary Prolog clause: person is a prefix operator applied to a braces term holding comma-separated name ..., age ... entries. The goal uses variables in the same syntax, so unification extracts every field. Note the space in person {: written without it, a name followed by braces is read by SWI-Prolog as a dict.

Exercise: Your own operator

Declare an infix operator likes (priority 700, type xfx) and add the facts mia likes tea and zoe likes coffee written with it. Then mia likes X must give X = tea.