Declarative Programming
Meta-programming in Prolog (2)
Geraint A. Wiggins
Professor of Computational Creativity
Department of Computer Science
Vrije Universiteit Brussel
Summary of previous lecture
I Meta-programming is programming where the data
represents a program
I We say that that data is the name of the program it
represents
I In Prolog, we usually use as a name a term which is
identical to the named program itself
I In Prolog, we usually use a non-ground representation –
i.e., we use variables to name variables
I A better approach is a ground representation – but this
has the disadvantage that we have to handle unification
and variable bindings explicitly
Summary of previous lecture (2)
I It is possible to represent the Prolog execution
mechanism as a meta-program, which refers directly to
the Prolog database
I It is possible to extend and adapt the execution
mechanism in various ways, such as to change the
execution rule, and to admit multiple databases
I Meta-programming can allow significant increases in the
reasoning power of the language, and can allow us to
solve complex problems merely by stating them as logic
programs
Prolog meta-programming facilities
I Meta-programming predicates in Prolog can be divided
into several headings:
I term/instantiation testing
I term manipulation
I database access
I database manipulation
I meta-level identity testing
Instantiation testing
I These predicates are used for testing if the meta-level
representation of a program variable (which is itself a
variable) has a value or not.
var(X) succeeds if X is an uninstantiated variable;
nonvar(X) succeeds if X is not an uninstantiated
variable;
ground(X) succeeds if X is a fully instantiated term.
I For example, a correct use of var/1 might be in an
explicit unification algorithm, where we need to know if
an object is a variable or a term
I They are often abused for preventing object-level calls to
predicates where instantiation is crucial, e.g.,
p( X, Y ) :- ground( Y ),
X is Y.
Syntax checking
I These predicates are used for testing the syntactic shape
of a meta-level term
var(X) succeeds if X is a variable;
atom(X) succeeds if X is a ground atom
float(X) succeeds if X is a ground floating-point
number (real/1 in some implementations)
integer(X) succeeds if X is a ground integer
number(X) succeeds if X is a ground number
atomic(X) succeeds if X is a number or an atom
simple(X) succeeds if X is either a variable or atomic
compound(X) succeeds if X is neither a variable nor
atomic
callable(X) succeeds if X is a well-formed Prolog goal
Prolog term manipulation
I These predicates are very useful for constructing
meta-level terms, but also as short-cuts for building
object-level terms when we really need to
functor(Term,Name,Arity) succeeds when Term has
functor Name and arity Arity
arg(ArgNo,Term,Arg) succeeds when Arg is the
Argnoth argument of Term
Term =.. List (univ) succeeds when List is of the
form [F|L] and F is the functor of Term and
L is a list of its arguments
?- p(a,b) =.. L.
L = [p,a,b]
true
I =../2 is the often the most useful meta-predicate, as it
can remove the need to write infinite numbers of clauses!
Prolog term manipulation (2)
name(Const,CharList) succeeds if Const is an atomic
constant whose name is spelled in CharList
I Notation: we use the term “abc” to denote the string
[97,98,99], which is the list of the ASCII codes for the
letters a, b, c
?- name( ’Geraint’, GList ),
name( ’Wiggins’, WList ),
append( GList, WList, Name ),
name( Whole, Name ).
Whole = ’GeraintWiggins’
true
?- name( ’Geraint’, “Geraint” ).
Prolog database access and manipulation
I Some of these predicates allow us to change the contents
of the Prolog database. If you do this in an arbitrary way,
you will probably damage the logic of your program
clause(Head,Body) succeeds if there is a clause
unifiable with Head :- Body. in the
database. If Head is a fact in the database,
then Body is set to true
assert(Clause) adds Clause to the database
asserta(Clause) adds Clause to the database as the
first clause of the predicate so defined
assertz(Clause) adds Clause to the database as the
last clause of the predicate so defined
retract(Clause) removes the first clause to match
Clause from the database
retractall(Head) removes all the clauses whose head
match Head from the database
Prolog database access/manipulation (2)
I In order to store (parts of) a predicate, the predicate
must be dynamic
I If you build a new predicate, Prolog will make it dynamic
automatically; otherwise you must declare it as dynamic:
:- dynamic p/1.
p(2).
?- assertz( p(1) ),
asserta( p(3) ), listing( p ).
p(3).
p(2).
p(1).
true
The Prolog internal database
I This is covered for historical reasons; there is now false
need to use this facility
I There is a special area of the Prolog database which is
reserved for storing persistent information: the internal
database.
I Each term stored is accessed via a key (for speed) and
assigned a reference number
recorded(Key,Term,Ref) succeeds if the Term is
recorded under Key with reference Ref
recorda(Key,Term,Ref) as for asserta – Ref must
be uninstantiated
recordz(Key,Term,Ref) ditto assertz
erase(Ref) succeeds if a term is stored against
reference Ref and it has been erased
Meta-level identity testing
I There are two predicates for testing syntactic identity at
the meta-level
==/2 succeeds if its arguments are syntactically
identical
\==/2 succeeds if its arguments are not
syntactically identical
I Note the difference between = and ==:
?- X = Y.
X = Y
true
?- X = X.
true
?= f(X) = f(1).
X = 1
true
?- X == Y.
false
?- X == X.
true
?- f(X) == f(1).
false
Meta-level identity testing (2)
I Note also that \== does not mean “not equals”; it means
“syntactically different from” and its result depends on
the instantiation of its variables
I For example,
?- \+ X = X.
false
?- \+ X = Y.
false
?- X \== X.
false
?- X \== Y.
true
I It is incorrect to use \== as object-level “not equals”
because it depends on instantiation
I In the next lecture, we will see how to solve the problems
with the proper object-level “not equals”