.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Declarative Programming
Non- & Meta-logical features of Prolog
Geraint A. Wiggins
Professor of Computational Creativity
Department of Computer Science
Vrije Universiteit Brussel
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Input/Output (IO)
▶ Input/Output is a problem for declarative programming
languages
▶ Output: we cannot give a truth value to the action of
printing something
▶ Input: we could, maybe, view input as coming from a
changing Prolog database, but this still gives us problems
in understanding what the program means
▶ The problems get worse when we get on to flexible
computation rules, later!
▶ In fact, there are ways to tell a declarative story about
I/O, but we will not cover them here.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
IO Facilities in Prolog
▶ Two ways of thinking about IO:
▶ file-based;
▶ stream-based
▶ File-based IO is nice and simple, but is not
well-thought-out and can be confusing
▶ Stream-based IO is more complicated, but much more
reliable
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
File-based IO in Prolog
▶ File-based IO is based on the idea of a “current file”
(which may be the terminal)
▶ You can have several files open at once, but you can only
read from or write to one at a time
▶ Each file has a pointer, which marks where the last
operation took place
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
File-based IO in Prolog (2)
▶ We have nine basic predicates:
see/1 choose the current reading file (and open it
if necessary)
seen/0 close the current reading file
seeing/1 is this the current reading file?
read/1 read a term from the current reading file
tell/1 choose the current writing file (and open it if
necessary)
told/0 close the current writing file
telling/1 is this the current writing file?
write/1 write a term to the current writing file
nl/0 write a newline to the current writing file
▶ The terminal is a file called user.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
File-based IO in Prolog (3)
| ?- tell(example).
true
| ?- write(example(term)), write( ‘.’ ).
true
| ?- told.
true
| ?- see(example).
true
| ?- seeing(File).
File = example ?
true
| ?- read(Term).
Term = example(term) ?
true
| ?- seen, seeing(File).
File = user ?
true
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Stream-based IO in Prolog
▶ We associate each open file with an explicit pointer, with
which we refer to it subsequently.
▶ There are too many predicates to learn usefully – see the
manual for details
▶ The basic stream handling predicates are:
open/3 Arguments: a file specification (+);
a mode (read/write/append) (+); a
pointer (-)
close/1 Argument: a pointer (+)
current_input/1 Argument: a pointer (?)
set_input/1 Argument: a pointer (-)
read/2 Arguments: a pointer (+); a term (?)
write/2 Arguments: a pointer (+); a term (?)
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
General IO in Prolog
▶ Most of the reading and writing predicates come in two
versions:
▶ to the current file/stream (eg write/1)
▶ to a named stream (eg write/2)
▶ The first argument is conventionally the stream pointer
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
The format Predicate
▶ The most useful writing predicates are format/2 and
format/3
▶ format/2 has two arguments:
▶ a format specification (atom or string);
▶ a list of arguments
▶ For example, the predicate call
format( ‘v\n~w – ~w+\n’,
[term1, term2] ).
will print out to the terminal:
v
term1 – term2+
▶ See the manual for more details of format specifications
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Arithmetic Operators
▶ Recall that =/2 means “unify”
▶ We need arithmetic functions, even in logic programs
▶ Here are the basic arithmetic predicates
is/2 computes the value of the arithmetic
expression in its second argument (+) and
unifies it with the first argument (?)
>= > =:= =\= < =2 all compute the respective comparison between the values of two arithmetic expressions (+,+) \= SWI Prolog includes an abbreviation for \+ ( … = … ): ▶ See the manual for details of arithmetic built-ins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Meta-Predicates ▶ Meta-predicates are predicates which work on data which is beyond the logic of Prolog ▶ Usually, they assign truth values to statements about the logic or the language ▶ They are mostly meant for programming where the data is a program… …but they are often abused to write hacky prolog code Do not do this here: you will lose marks. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Meta-Predicates (2) ▶ Example: var/1 ▶ var is a meta-predicate which tests whether or not its argument is a variable ▶ it is a meta-predicate because a variable is not a term – it may contain one, but it is not one… …so this predicate works on part of the Prolog language syntax, not on the program’s internal logic. ▶ var/1 might be used in an implementation of unification, to decide whether a variable had already been unified or not ▶ It should never be used in an object-level program (i.e., a program whose data is not Prolog code) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Misusing meta-predicates (3) ▶ Here is how NOT to use ground/1, which succeeds if its argument is fully instantiated ▶ We want to test if two expressions are equal: equal( X, Y ) :- ground( X ), ground( Y ), Y =:= X. equal( X, Y ) :- ground( X ), \+ ground( Y ), Y is X. equal( X, Y ) :- \+ ground( X ), ground( Y ), X is Y. equal( X, Y ) :- \+ ground( X ), \+ ground( Y ), format( 'Error!\n', [] ). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . To meta-program or not? ▶ Rule of thumb 1: if you have to use a meta-predicate and you’re not sure why, there is something wrong. ▶ Rule of thumb 2: if you have to use a meta-predicate and your data is not (part of) a program clause, there is something wrong. ▶ Do not ever use meta-predicates to control the run-time behaviour of Prolog (as in the example), because ▶ It messes up the logic of your program; ▶ It makes your program very hard to analyse automatically; ▶ It makes the compiler work sub-optimally; ▶ There are much better ways to do it. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Some safe, useful meta-predicates ▶ There are three meta-predicates which are very useful and mostly safe logically ▶ They are concerned with finding multiple solutions to queries ▶ findall/3 is exactly analogous to running a query and asking for all the solutions by hitting ; at the prompt ▶ For example, given a definition of ancestor/2, findall( X, ancestor( john, X ), Answers ) instantiates Answers with a list of all the Xs such that ancestor( john, X ) is provable in the current database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Some safe, useful meta-predicates (2) ▶ findall/3 is very much a proof based predicate – it simulates exactly what Prolog does under user control ▶ Answers appear in the list in the order that they would at the terminal ▶ NB this means that if there are infinitely many solutions, it will never terminate! (or rather, you’ll get a stack or heap overflow error) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Some safe, useful meta-predicates (3) ▶ Two more logically-orientated meta-predicates are setof/3 and bagof/3 ▶ setof/3 instantiates its third argument to a list which represents an ordered set of all the answers generated for the term given in argument 1 by the goal given in argument 2 ▶ As with findall/3, compound structures can be given in argument 1, and compound goals in argument 2: setof( Place-Distance, ( distance( brussels, Place, Distance ), nice_to_visit( Place )), Possible_Venues ). ▶ bagof/3 is the same as setof/3, but returns a multiset of answers (i.e., multiple answers appear multiple times) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Some safe, useful meta-predicates (4) ▶ findall/3 differs from the other two in its treatment of uninstantiated variables in its goal which are not named in argument 1 ▶ It assumes that all variables in argument 2 which are not named in argument 1 are existentially quantified ▶ This means that they can take any (i.e., more than one) value, while the answers are generated ▶ setof/3 and bagof/3 require explicit existential quantification with the ^ operator ▶ Otherwise, each of the free variables takes a fixed value, with setof/3 or bagof/3 backtracking to produce all the possible answers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Some safe, useful meta-predicates (5) ▶ For example, these queries produce the same answers (but maybe not in the same order): findall( X, p( X, Y ), L ). bagof( X, Y^p( X, Y ), L ). ▶ but these may not (it depends on the definition of p/2) findall( X, p( X, Y ), L ). bagof( X, p( X, Y ), L ). ▶ Note that if Y has a value when these goals are called, there is no difference in any of the behaviours . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Some safe, useful meta-predicates (6) ▶ For another example, consider the following program and query: p( a, b ). p( c, b ). p( d, e ). ?- setof( X, p( X, Y ), L ). L = [a,c], Y = b ? ; L = [d], Y = e ? ; false