SICP 8a
Logic Programming I
Table of Contents
1 Recap
The cycle of eval
and apply
unwinds the means of combination and the means
of abstraction in the language. This cycle is the basic structure of any
interpreter. Once you have a basic idea of an interpreter, you can build any
language.
Inventing a new language can be a method to gain control over complexity. Computer programming is primarily a way to express ideas, and only incidentally has to do with getting a computer to do something. To communicate new kinds of ideas, new modes of expression are required.
2 Agenda
In this lecture, and the following one, we will build a very different language, which won't allow us to think about programming in terms of procedures.
Up next:
Show what the language looks like.
Show how the language is implemented.
Thirdly, we'll demonstrate a nice use of streams to avoid backtracking.
3 Declaritive/imperative knowledge
In the first set of lectures, we covered the distinction between the declarative knowledge of mathematics, and the imperative knowledge of computer science. Can this gap be bridged?
son-of adam abel son-of adam cain son-of cain enoch son-of enoch irad
One fact son-of adam cain
can be used to answer multiple different
questions: what is the relationship between adam
and cain
? Who is the son
of adam
? Who is cain
a son of?
One piece of declarative knowledge can be used as the basis of many different kinds of imperative knowledge; this is the essence of the power of this programming style.
Rules of inference.
if (son-of ?x ?y) and (son-of ?y ?z) then (grandson ?x ?z)
Let's take a step back, and look at a procedure for merging two sorted lists.
(define (merge x y) (cond ((null? x) y) ((null? y) x) (else (let ((a (car x)) (b (car y))) (if (< a b) (cons a (merge (cdr x) y)) (cons b (merge x (cdr y))))))))
Below is the specific bit of logic which allows the program to work.
(let ((a (car x)) (b (car y))) (if (< a b) (cons a (merge (cdr x) y))
A deduction is happening here.
if (cdr-x and y merge-toform z) and a < (car y) then ((cons a cdr-x) and y merge-to-form (cons a z))
The other clause is essentially identical.
if (x and cdr-y merge-to-form z) and b < (car x) then (x and (cons b dry-y) merge-to-form (cons b z))
And for completeness, we have these two basic statements.
For all x, (x and () merge-to-form x) For all y, (() and y merge-to-form y)
The procedural approach could be visualised with merge
as a black box, taking
the inputs x
and y
, and outputting ans
. The rule-based approach, in
juxtaposition, would be visualised as a cloud containing merge-to-form
, x
,
y
and z
.
With these fundamental rules of logic, it should be possible to ask:
(1 3 7) and (2 4 8) merge-to-form ? (1 3 7) and ? merge-to-form (1 2 3 4 7 8) ?x and ?y merge-to-form (1 2 3 4 7 8)
There are two major differences to procedural programming which are immediately apparent.
Not computing functions (using relations instead).
Due to the use of relations, there's not necessarily one answer.
This style of programming is called logic programming. Use logic to express what is true, use logic to check if something is true, use logic to find out what is true.
An example of a logic programming languages is Prolog. We will implement a query language, which will contain the essence of Prolog, and roughly the same power and limitations.
4 Facts are facts
Let's start building and begin with a collection of facts.
(job (Bitdiddle Ben) (computer wizard)) (salary (Bitdiddle Ben) 40000) (supervisor (Bitdiddle Ben) (Warbucks Oliver)) (address (Bitdiddle Ben) (Slumerville (Ridge Road) 10)) (address (Hacker Alyssa P) (Cambridge (Mass Ave) 78)) (job (Hacker Alyssa P) (computer programmer)) (salary (Hacker Alyssa P) 35000) (supervisor (Hacker Alyssa P) (Bitdiddle Ben)) (address (Tweakit Len E) (Boston (Bay State Road) 22)) (job (Tweakit Len E) (computer technician)) (salary (Tweakit Len E) 15000) (supervisor (TweakitLen E) (Bitdiddle Ben)) (address (Reasoner Louis) (Slumerville (Pine Tree Road) 80)) (job (Reasoner Louis) (computer programmer trainee)) (salary (Reasoner Louis) 20000) (supervisor (Reasoner Louis) (Hacker Alyssa P)) (job (Warbucks Oliver) (administation big wheel)) (salary (Warbucks Oliver) 100000) (address (Warbucks Oliver) (Swellesley (The Manor)))
As with any language, there are three fundamental questions.
What are the primitives?
What are the means of combination?
What are the means of abstraction?
5 Primitives
There is one primitive: query.
(job ?x (computer programmer)) results: (job (Hacker Alyssa P) (computer programmer)) (job ?x (computer ?type)) results: (job (Bitdiddle Ben) (computer wizard)) (job (Hacker Alyssa P) (computer programmer)) (job (Tweakit Len E) (computer technician))
However it doesn't match:
(job (Reasoner Louis) (computer programmer trainee))
as Louis' job description has three symbols.
The addition of a full stop rectifies the above issue.
(job ?x (computer . ?type))
6 Means of combination
List all people who work in the computer division, together with their supervisors.
(and (job ?x (computer . ?y)) (supervisor ?x ?z))
List all people whose salary is greater than 30000.
(and (salary ?p ?a) (lisp-value > ?a 30000))
List all people who work in the computer division, who do not have a supervisor who works in the computer division.
(and (job ?x (computer . ?y)) (not (and (supervisor (?x ?z) (job ?z (computer . ?w))))))
The means of combination are logical operations: and
, not
, or
and a hack,
lisp-value
.
7 Means of abstraction
Rules are the means of abstraction.
(rule (bigshot ?x ?dept) ; rule conclusion (and ; rule body (job ?x (?dept . ?y)) (not (and (supervisor ?x ?z) (job ?z (?dept . ?w))))))
Revisit merge
in terms of rules.
(rule (merge-to-form () ?y ?y)) (rule (merge-to-form ?y () ?y))
A rule with no body is always true.
(rule (merge-to-form (?a . ?x) (?b . ?y) (?b . ?z)) (and (merge-to-form (?a . ?x) ?y ?z) (lisp-value > ?a ?b))) (rule (merge-to-form (?a . ?x) (?b . ?y) (?a . ?z)) (and (merge-to-form ?x (?b . ?y) ?z) (lisp-value > ?b ?a)))
Now test.
(merge-to-form (1 3) (2 7) ?x) results: (merge-to-form (1 3) (2 7) (1 2 3 7)) (merge-to-form (2 ?a) ?x (1 2 3 4)) results: (merge-to-form (2 3) (1 4) (1 2 3 4)) (merge-to-form (2 4) (1 3) (1 2 3 4)) (merge-to-form ?x ?y (1 2 3 4 5))
With the last query, the slowness is blatantly evident. This is partly due to the language being doubly interpreted, but also due to the algorithm for merges being doubly recursive.