% PLL: Programming in Logic in Lisp --- header-includes: --- # PLL: Programming in Logic in Lisp PLL is a simple Prolog in Scheme, developed as a teaching aid. It is supposed to make it easy to understand the execution model of Prolog, including variables, cuts, and meta-predicates. There is also a simple FFI to Scheme. When I say "simple", I really mean it. This implementation is NOT efficient at all, and it misses several features. The manual is also very basic, although it covers most of what really matters. The code is documented, but not "densely documented" -- this is mostly because the interpreter was developed as an auxiliary tool for a course, and the implementation details are explained in the course notes which were written in Portuguese. However, the code may be used as a companion to chapter 4 of Sterling and Shapiro's "The Art of Prolog", as it closely follows the execution model explained there. ## LOADING PLL There are two ways to use PLL. In implementations that support R7RS, you can import it. This may be useful if you want to rename identifiers. In implementations that do not support R7RS, there is a file that will load the interpreter. ``` (load "pll-standalone.scm") ;; this works on all supported schemes. (import (pll)) ;; only on the systems marked as having ;; support for R7RS modules ``` ## VARIABLES Traditionally, Prolog systems treat symbols beginning with uppercase letters as variables: X, Y, Variable are variables, while x, y, constant are constants. Lisp-based Prologs usually do this differently: the variables are the symbols that begin with a question mark: `?x`, `?y`, `?variable`, etc. We do the same in this implementation. ## CALLING THE INTERPRETER Our Prolog use the following syntax: ``` (pure-prolog PROGRAM GOAL) ``` The program is a list of assertions. The program ``` p(x). p(Z) :- q, r(Z). ``` is written as a list of assertions. Each assertion is a list where the head represents the left side (the consequent), and the tail is a list of the goals to be met in order to prove the consequent. For example, ``` '(( (p x) )) ( (p ?z) q (r ?z))) ``` The GOAL is a list of goals: ``` p(1), q(X). ``` is written as ``` '((p 1) (q ?x)) ``` EXAMPLE: Suppose we want to enter the following program: ``` f(0). f(Z) :- g(Z). h(3). h(4). p(Z,Y,S) :- f(Z),g(Y),h(S) g(10). ``` And ask the question ``` p(10,D,A), q(A). ``` We can do this: ``` (define facts '(( (f 0) ) ( (f ?z) (g ?z) ) ( (h 3) ) ( (h 4) ) ( (q 4) ) ( (p ?z ?y ?s) (f ?z) (g ?y) (h ?s) ) ( (g 10) ))) (define goals '((p 10 ?d ?a) (q ?a))) ``` And call ``` (pure-prolog facts goals) ``` Or, directly enter the facts and goal (as we do in the examples that are included in `prolog-examples.scm`): ``` (pure-prolog '(( (f 0) ) ( (f ?z) (g ?z)) ( (h 3) ) ( (h 4) ) ( (q 4) ) ( (p ?z ?y ?s) (f ?z) (g ?y) (h ?s)) ( (g 10) )) '((p 10 ?d ?a) (q ?a))) ``` The result will be a list of substitutions that satisfy the goal: ``` ((?a . 4) (?d . 10)) ``` GETTING MULTIPLE ANSWERS ------------------------ This can be done with the `amb+` operator. ``` (pure-prolog '(((f 1)) ((f 2))) '((f ?x))) ((?x 1)) ``` If we call `(amb+)`, then we get another solution: ``` ((?x 2)) ``` ## DIFFERENT INTERPRETERS The different interpreters included are: - `pure prolog` (prolog only) - `prolog+built-ins` (with built-in predicates with side-effect) - `prolog+scheme` (with an FFI to Scheme, but NO built-ins) - `prolog+local` (with "IS" and local vars; on top of prolog+scheme) - `prolog+meta` (with assert and retract; on top of prolog+local) - `prolog+cut` (with cut (!); on top of prolog+meta) So the features are added one on top of the other, except for the built-ins feature, which is not included in the later interpreters. In the source code, there is one initial implementation (pure-prolog) with short comments explaining how it works. Each interpreter after that one has comments only on the modified parts. ### Pure Prolog This is the most basic of all. You can only statet facts as we described above, nothing else. Call it as ``` (pure-prolog facts goals) ``` For example, we define a graph by declaring its edges, then define a `reach/2` predicate. ``` edge(a,b). edge(a,c). edge(c,b). edge(c,d). edge(d,e). reach(A,B) :- edge(A,B). reach(A,B) :- edge(A,X), reach(X,B). ``` We could then as "`reach(b,e)`" and find that *b* doesn't reach *e*. In PLL: ``` (define graph '( ((edge a b)) ((edge a c)) ((edge c b)) ((edge c d)) ((edge d e)) ((reach ?a ?b) (edge ?a ?b)) ((reach ?a ?b) (edge ?a ?x) (reach ?x ?b)))) (pure-prolog graph '( (reach b e) )) #f ``` ### Prolog with built-in "procedures" This adds some predicates with side-effects. Define a list of Scheme functions that you want to be accessible from Prolog: ``` (define write-it (lambda (args sub) (cond ((not (null? args)) (if (matching-symbol? (car args)) (display (assoc (car args) sub)) (display (car args))) (write-it (cdr args) sub)) (else #t)))) ``` Here "`sub`" is the current substitution, where Prolog will find the values of variables. ``` (define built-ins `((write . ,write-it))) ``` So `(write a b c ...)` will be translated to ``` (write-it (a b c ...) sub) ``` Then, whenever one of these predicates show up in the goal, Prolog will execute them: ``` father(john,johnny). father(old-john,john). grandpa(A,B) :- father(A,X), father(X,B). ``` Our goal is ``` grandpa(old-john,Who), write("grandson is: "), write(Who). ``` In our Prolog, this is expressed as follows: ``` (prolog+built-ins '( ((father john johnny)) ((father old-john john)) ((grandpa ?a ?b) (father ?a ?x) (father ?x ?b)) ) '( (grandpa old-john ?who) (write "Grandson is: " ?who) )) Grandson is: johnny (((?who . johnny)) ()) ``` The first line was the effect of having a `(write ...)` in the goal. The second is the answer. ### Prolog with any scheme function This interpreter has no sandbox -- it will recognize and execute any Scheme function when it sees one. Suppose we want to run this Prolog program, and use a Scheme function "`print`" in the goal: ``` a(5). b(3). b(5). ``` Goal: ``` a(X), b(Y), X=Y, print(X, " " Y). ``` In our Prolog, we have: ``` (prolog+scheme '( ((a 5)) ((b 3)) ((b 5))) '( (a ?x) (b ?y) ((= ?x ?y)) ((print ?x " " ?y)))) 5 5 ((?y . 5) (?x . 5)) ``` The first line is the effect of the print. The second line is Prolog's answer. ### With local variables ``` (prolog+local '( ((f ?x ?y) (is ?y (* 2 ?x)) ((print 'OK: ?y)))) '( (f 3 ?a) )) OK:6 ((?a . 6)) ``` ### With assert and retract This adds the meta-predicates `assert` and `retract`. ``` (prolog+meta '(( (f ?x) (asserta (h 1))) ( (g ?x) (h ?x))) '((f ?w) (g ?z))) ``` The result will be `((?z . 1))` `retract` has the opposite effect. The Prolog program we show now is ``` f(2). f(3). g(1) :- retract(f(2)). ``` And the goal is ``` g(1), f(X). ``` In PPL's Schemish-Prolog, this is ``` (define prolog-retract '( ((f 2)) ((f 3)) ((g 1) (retract ((f 2)))) ((g 1) (f 3)))) (prolog+meta prolog-retract '((g 1) (f ?x))) ``` `(g 1)` causes retract to be evaluated, so the goal succeeds with *X=3*, and not *X=2*: ``` ((?x 3)) ``` ### With cut This adds the cut operator. For example, in Prolog we could write ``` a(X) :- b(X), !, c(X). b(1). b(2). c(2). ``` The goal `a(X)` fails, because after the interpreter chooses `b(1)`, it cannot backtrack, and `c(1)` is not true. In our Prolog, we write: ``` (prolog+cut '(( (a ?x) (b ?x) ! (c ?x) ) ( (b 1) ) ( (b 2) ) ( (c 2) )) '((a ?x))) #f ``` If we remove the cut, then this goal will succeed with *X=2*. The cut can be used also in the goal. ## BUGS AND MISSING FEATURES * There is no way to save and load the database * There is no simple way to get all possible answers (substitutions) for a query in a list. * This manual is too short. ## A BIBLIOGRAPHY The following is a list of some books related to Prolog programming and implementations of Prolog. 1. ***William Clocksin, Chris Mellish***. *"Programming in Prolog"*. Springer, 2003. [ a very basic text ] 2. ***Michael Covington, Donald Nute, André Vellino***. *"Prolog Programming in Depth"*. Scott, Foresman and Company, 1988. [ quick introduction to Prolog, followed by some advanced programming techniques and application ] 3. ***Leon Sterling, Ehud Shapiro***. *"The Art of Prolog"*. MIT Press, 1994. [ solid introduction to Prolog, including the execution model -- strongly recommended for those who want to understand the internals of the interpreter ] 4. ***Richard O'Keefe***, *"The Craft of Prolog"*. MIT Press, 1990. [ advanced programming techniques ] 5. ***Harold Abelson, Gerald Jay Sussman***. *"Structure and Interpretation of Computer Programs"*. Addison-Wesley, 1996. [ explains and implements in Scheme the AMB operator and a small Prolog interpreter ] 6. ***Peter Norvig***. *"Paradigms of Artificial Intelligence Programming"*. Morgan Kaufmann, 1992. [ part of the book explains unification and contains another Prolog implementation, in Common Lisp ] 7. ***Jacques Chazarain***, *"Programmer Avec Scheme"*. International Thomson Publishing France, 1996 (in French). [ a very good book on Scheme. chapters 15-19 focus on Prolog ] 8. ***J. A. Campbell***. *"Implementations of PROLOG"*. Ellis Horwood, 1984. [ a study of implementation techniques. quite advanced. ]