Docstoc

Declarative languages Graph reachability in Prolog

Document Sample
Declarative languages Graph reachability in Prolog Powered By Docstoc
					Declarative languages                                         Graph reachability in Prolog
  • Programming in Prolog                                                    C                  F
  • Implementing Prolog: ideas                                    A                  D
        – With continuations
                                                                                                          K          H
        – Without continuations

                                                                                 E
                                                                  B
                                                                                            J




CS 798 Fall 2009         09: Declarative languages        1   CS 798 Fall 2009           09: Declarative languages       2




edge(a,c).                                                    Logic variables start with capital letters.
edge(a,d).
edge(a,e).                                                    reach(X,Y) :- edge(X,Y).
edge(b,e). ...                                                reach(X,Y) :-
                                                                      edge(X,Z),
                                                                      reach(Z,Y).
edge is a 2-ary relation.
                                                              sibling(X,Y) :-
a,b,. . . are 0-ary relations or constants.                           edge(Z,X),
The above lines describe facts in the edge relation.                  edge(Z,Y),
                                                                      X \== Y.
We can now add rules describing other relations.
A Prolog interpreter lets us pose queries for solution.




CS 798 Fall 2009         09: Declarative languages        3   CS 798 Fall 2009           09: Declarative languages       4
sort(Xs,Ys) :- permutation(Xs,Ys), ordered(Ys).    Type checking using Prolog
ordered([X]).                                      We define rules for a 3-ary relation type.
ordered([X,Y|Ys]) :- X<Y, ordered([Y|Ys]).
                                                   type(TEnv,P,T) is true when TEnv                   P : T , that
permutation(Xs,[Z|Zs]) :- select(Z,Xs,Ys),         is, we can conclude using type environment TEnv that P
                          permutation(Ys,Zs).
permutation([],[]).                                has type T.

select(X,[X|Xs],Xs).                               We’ll assume that P has been parsed so that constants are
select(X,[Y|Ys],[Y|Zs]) :- select(X,Ys,Zs).        represented by their types.

?- sort([5,3,4,2,1],S).




CS 798 Fall 2009   09: Declarative languages   5   CS 798 Fall 2009       09: Declarative languages                  6




type(_,numConst,num).                              We can now type-check some programs:
type(_,boolConst,bool).                            :- type([],numConst,num).
type(TEnv,if(Test,Then,Else),Tau) :-               :- type([],
        type(TEnv,Test,bool),                              if(boolConst,numConst,numConst),
        type(TEnv,Then,Tau),                               num).
        type(TEnv,Else,Tau).                       :- type([],
type([bind(V,T)|_],vbl(V),T).                              app(fun(x,if(var(x),numConst,numConst)),
type([bind(_,_)|TEnvRest],vbl(V),T) :-                         boolConst),
        type(TEnvRest,vbl(V),T).                           num).
type(TEnv,fun(Vbl,Body),arrow(T1,T2)) :-
        type([bind(Vbl,T1)|TEnv],Body,T2).         But we can also do type inference.
type(TEnv,app(Fun,Argt),T2) :-                     :- type([],
        type(TEnv,Fun,arrow(T1,T2)),                       app(fun(x,if(var(x),numConst,numConst)),
        type(TEnv,Argt,T1).                                    boolConst),
                                                           T).
                                                   :- type([],fun(x,var(x)),T).


CS 798 Fall 2009   09: Declarative languages   7   CS 798 Fall 2009       09: Declarative languages                  8
Implementing Prolog                                               A logic variable is bound to a likely value, but this binding is
                                                                  tentative pending verification of the rest of the rule.
Prolog facts can be stored in a flat relational database.
                                                                  If, for some reason, that verification fails, the
Prolog rules are in DNF (“or of ands”).
                                                                  implementation backtracks to this point and tries another
A query, or goal, forms a search tree, with an or at the root.
                                                                  value of the logic variable.
Each possibility forms a sub-goal which may involve
                                                                  Even if the verification succeeds, the backtracking point is
further searching.
                                                                  preserved in case another solution is sought.
The search is depth-first, for efficiency reasons.
But what role do logic variables play?




CS 798 Fall 2009        09: Declarative languages             9   CS 798 Fall 2009        09: Declarative languages            10




Using Scheme continuations                                        Each Prolog expression thus consumes a failure
                                                                  continuation and produces a success continuation (if the
When the program tries to satisfy an and-clause with a
                                                                  success continuation is not invoked).
binding to one or more logic variables, it does so within a
let/cc capturing a success continuation. This is invoked if       We can understand this process better with an example of

the search within the clause succeeds.                            a simpler backtracking construct.

But before that search starts, a failure continuation is          The amb construct was proposed by John McCarthy in

captured, which is invoked if the search fails.                   1961.

The failure continuation must unbind the logic variables
and try the next possible binding.




CS 798 Fall 2009        09: Declarative languages          11     CS 798 Fall 2009        09: Declarative languages            12
The amb special form                                               The criterion for amb succeeding is a little more strict.

(amb) fails.                                                       It must produce the value that makes the whole program
                                                                   succeed.
amb with one or more arguments produces the value of the
first one that succeeds.                                            (if (amb #t #f) (amb) 4) produces 4, because the first amb
                                                                   expression produces #f.
(amb 1 2) produces 1.
                                                                   We use the backtracking-with-continuations idea to
(amb (amb) (amb) 3) produces 3.
                                                                   implement amb.




CS 798 Fall 2009            09: Declarative languages         13   CS 798 Fall 2009                  09: Declarative languages      14




We maintain the current failure continuation with a mutable        (define-syntax amb
global variable.                                                      (syntax-rules ()
                                                                          [(amb) (amb-fail)]
(define amb-fail #f)
                                                                          [(amb alt) alt]
(define (init-amb-fail)
                                                                          [(amb alt1 alt2)
   (set! amb-fail
                                                                               (let ([prev-amb-fail amb-fail])
             (lambda () (error ’amb "choices exhausted"))))
                                                                                      (let/cc sk
(init-amb-fail)
                                                                                        (let/cc fk
                                                                                          (set! amb-fail fk) (sk alt1))
                                                                                        (set! amb-fail prev-amb-fail) alt2))]
                                                                          [(amb alt1 alts · · · ) (amb alt1 (amb alts · · · ))]))


CS 798 Fall 2009            09: Declarative languages         15   CS 798 Fall 2009                  09: Declarative languages      16
(define-syntax amb                                                   (define (assert pred) (unless pred (amb)))
   (syntax-rules ()
       [(amb alt · · · )                                            (define (count-from-to n m)
            (let ([prev-amb-fail amb-fail])                            (if (> n m) (amb) (amb n (count-from-to (add1 n) m))))
                   (let/cc sk
                                                                    (define (get-evens n)
                     (let/cc fk
                                                                       (define value (count-from-to 1 n))
                       (set! amb-fail fk)
                                                                       (assert (even? value))
                       (sk alt)) · · ·
                                                                       value)
                     (set! amb-fail prev-amb-fail)
                     (amb-fail)))]))
                                                                    (get-evens 30)



CS 798 Fall 2009                   09: Declarative languages   17   CS 798 Fall 2009         09: Declarative languages          18




Back to Prolog                                                      In our generalization of amb to handle the search at an or

In matching a goal such as edge(a,X) against a fact                 node, a failure means we must unbind the logic variables

edge(a,d), it is clear how to bind X.                               whose substitution was found by one unification problem,
                                                                    and try again with the next sibling node (or invoke the
The binding of logic variables may be nontrivial if the match
                                                                    failure continuation provided by the parent).
is more complex.
Example: the goal                                                   Prolog offers some features which compromise purity for

type([bind(V,T)|_],vbl(V),T) and the rule                           the sake of efficiency.

type([bind(x,num)],vbl(x),num).
This is a unification problem (though the algorithm is made
more complicated because the relations involved change
with each program).

CS 798 Fall 2009                   09: Declarative languages   19   CS 798 Fall 2009         09: Declarative languages          20
The occurs check is omitted (or at least made optional).        Implementation without continuations
Prolog also provides the cut operator !, which accepts all      Can we do all this without let/cc?
choices made so far in that rule.
                                                                We could construct the success and failure continuations
member(X,[X|_]) :- !.
                                                                ourselves.
member(X,[_|Y]) :- member(X,Y).
                                                                Passing these in our interpreter lets us add amb or similar
This prevents needless searching of the rest of a list in the   constructs to the interpreted language.
case where member(P,Q) is part of an and, one of
                                                                But we can go further.
whose later parts fails.




CS 798 Fall 2009           09: Declarative languages       21   CS 798 Fall 2009         09: Declarative languages       22




Replacing failure by a list of                                  We represent the relation R by a function which consumes
successes                                                       x and produces a list of y such that R(x, y).
Philip Wadler, in a classic paper, shows that laziness          We will build such relations using a few combinators
permits a natural approach to logic programming (among          (functions consuming and producing functions).
other problems).                                                We start with a couple of base cases.
A list can be “constructed” in such a way that its elements
                                                                (define (fail x) empty)
are not evaluated until needed, replacing backtracking.
Friedman, Bird, and Kiselyov refine this idea in Scheme in       (define (succeed x) (list x))
their book “The Reasoned Schemer”.
We will consider a simple version of their approach.


CS 798 Fall 2009           09: Declarative languages       23   CS 798 Fall 2009         09: Declarative languages       24
Next: disjunction (“or”) and conjunction (“and”).                Some simple examples:

(define (disj f1 f2)                                              ((disj fail fail) 1) ⇒ empty
   (lambda (x)                                                   ((disj fail succeed) 1) ⇒ ’(1)
       (append (f1 x) (f2 x))))                                  ((disj succeed succeed) 1) ⇒ ’(1 1)
                                                                 ((conj fail fail) 1) ⇒ empty
(define (conj f1 f2)                                              ((conj fail succeed) 1) ⇒ empty
   (lambda (x)                                                   ((conj succeed succeed) 1) ⇒ ’(1)
       (apply append (map f2 (f1 x)))))




CS 798 Fall 2009                09: Declarative languages   25   CS 798 Fall 2009         09: Declarative languages         26




((disj                                                           Next we introduce logic variables. We will later put these in
     (disj fail succeed)                                         lists, so we use Scheme vectors to store them (and nothing
     (conj                                                       else).
         (disj (lambda (x) (succeed (+ x 1)))
                                                                 (define (var name) (vector name)) ;; name will be a symbol
                   (lambda (x) (succeed (+ x 2))))
                                                                 (define var? vector?)
         (disj succeed succeed)))
 100)                                                            As we did with type inference, we store substitutions in
⇒ ’(100 101 101 102 102))                                        association lists.

                                                                 (define empty-subst empty)
                                                                 (define (ext-s var value s) (cons (list var value) s))



CS 798 Fall 2009                09: Declarative languages   27   CS 798 Fall 2009         09: Declarative languages         28
To avoid having to generate some representation of fresh            To avoid the need to save and restore bindings in the case
logic variables, we use the fact that new uses of vector (as        of failure, we do not propagate substitutions.
with cons) results in a memory allocation.                          For example, we choose not to simplify the substitution
The Scheme predicate eq? tests for “pointer equality”.              ’((#(x) #(y)) (#(y) 1)) to ’((#(x) #(1)) (#(y) 1)).
We use this instead of equal? when comparing keys in the            This makes lookups a potentially multi-stage process.
association list.
                                                                    (define (lookup var s)
This means that two uses of (var ’x) will refer to different           (cond
variables. We need to bind a name to one and use it                        [(not (var? var)) var]
consistently to refer to the same variable.                                [(assq var s) ⇒ (lambda (b) (lookup (second b) s))]
                                                                           [else var]))


CS 798 Fall 2009        09: Declarative languages              29   CS 798 Fall 2009            09: Declarative languages        30




(define vx (var ’x))                                                 (define (unify t1 t2 s)
(define vy (var ’y))                                                    (define t1v (lookup t1 s))
(lookup vx ((,vx 1))) ⇒ 1                                              (define t2v (lookup t2 s))
(lookup (var ’x) ((,vx 1))) ⇒ (var ’x)                                 (cond
(lookup vx ((,vx ,vy) (,vy 1))) ⇒ 1                                        [(eq? t1v t2v) s]
                                                                           [(var? t1v) (ext-s t1v t2v s)]
Next we tackle unification. With types, we permitted an
                                                                           [(var? t2v) (ext-s t2v t1v s)]
“arrow” type constructor. Here we permit cons.
                                                                           · · · ))
But we may cons one logic variable onto another, so we
must use car and cdr instead of first and rest (which insist         The case where both terms use cons avoids needless
on a list argument).                                                work in case of an early failure.


CS 798 Fall 2009        09: Declarative languages              31   CS 798 Fall 2009            09: Declarative languages        32
(define (unify t1 t2 s)                                               (unify vx vy empty-subst) ⇒ ‘((,vx ,vy))
   (define t1v (lookup t1 s))
   (define t2v (lookup t2 s))                                         (unify vx 1 (unify vx vy empty-subst)) ⇒ ‘((,vy 1) (,vx ,vy))
   (cond · · ·
       [(and (cons? t1v) (cons? t2v))                                (lookup vy (unify vx 1 (unify vx vy empty-subst))) 1

            (local [(define fu (unify (car t1v) (car t2v) s))]
                                                                     (unify (cons vx vy) (cons vy 1) empty-subst)
                   (if (boolean? fu)
                                                                        ⇒ ‘((,vy 1) (,vx ,vy))
                     false
                     (unify (cdr t1v) (cdr t2v) fu)))]
       [(equal? t1 t2) s]
       [else false]))


CS 798 Fall 2009                  09: Declarative languages     33   CS 798 Fall 2009            09: Declarative languages      34




(define (== t1 t2)                                                    (run (disj (== vx 1) (== vx 2))
   (lambda (s)                                                       ⇒ ‘(((,vx 1)) ((,vx 2))))
       (cond
           [(unify t1 t2 s) ⇒ succeed]                               (run (conj (== vx 1) (== vx 2))) ⇒ empty
           [else (fail s)])))
                                                                     (run (conj (== vx 1) (== vx vy)))
(define (run g) (g empty-subst))                                      ⇒ ‘(((,vy 1) (,vx 1))))

We can now create simple queries.                                    (run (conj (== vx 1)
                                                                             (conj (== vx vy) (== vy 2)))
                                                                     ⇒ empty


CS 798 Fall 2009                  09: Declarative languages     35   CS 798 Fall 2009            09: Declarative languages      36
(define (choice var lst)                                                 (define (common-el lst1 lst2)
   (cond                                                                   (conj (choice vx lst1) (choice vx lst2)))
       [(empty? lst) fail]
                                                                        (run (common-el ’(1 2 3) ’(3 4 5))) ⇒ ‘(((,vx 3)))
       [else (disj (== var (first lst))
                   (choice var (rest lst)))]))                          (run (common-el ’(1 2 3) ’(3 4 1 7))) ⇒ ‘(((,vx 1)) ((,vx 3)))


(run (choice 2 ’(1 2 3))) ⇒ ‘(,empty-subst))                            (run (common-el ’(1 2 3) ’(4 5 6))) ⇒ empty

(run (choice 4 ’(1 2 3))) ⇒ empty                                       (define (cons-r a b lst) (== (cons a b) lst))
(run (choice vx ’(1 2 3)))
⇒ ‘(((,vx 1)) ((,vx 2)) ((,vx 3))))                                     (run (cons-r 1 ’(2 3) vx)) ⇒ ‘(((,vx (1 2 3)))))

                                                                        (run (cons-r vx vy ’(1 2 3))) ⇒ ‘(((,vy (2 3)) (,vx 1))))

CS 798 Fall 2009             09: Declarative languages             37   CS 798 Fall 2009                  09: Declarative languages   38




Defining an append relation brings up some interesting                   (define (append-r1 front rear all)
issues.                                                                    (disj (conj (== front empty) (== rear all))

Here’s how we might write it in PLAI Scheme.                                        (local [(define ff (var ’ff))
                                                                                              (define rf (var ’rf))
(define (is-append? front rear all)
                                                                                              (define ra (var ’ra))]
   (cond
                                                                                           (conj
       [(empty? front) (equal? rear all)]
                                                                                             (cons-r ff rf front)
       [else (and (equal? (first front) (first all))
                                                                                             (conj
                   (is-append? (rest front) rear (rest all)))]))
                                                                                               (cons-r ff ra all)
                                                                                               (append-r1 rf rear ra))))))

                                                                        This doesn’t work.

CS 798 Fall 2009             09: Declarative languages             39   CS 798 Fall 2009                  09: Declarative languages   40
(define (append-r2 front rear all)                                           (define-syntax conj∗
   (disj (conj (== front empty) (== rear all))                                 (syntax-rules ()
            (local [(define ff (var ’ff))                                           ((conj∗) succeed)
                      (define rf (var ’rf))                                         ((conj∗ g) g)
                      (define ra (var ’ra))]                                        ((conj∗ g gs · · · )
                   (conj                                                               (conj g (lambda (s) ((conj∗ gs · · · ) s))))))
                     (cons-r ff rf front)
                     (conj
                       (cons-r ff ra all)
                       (lambda (s) ((append-r2 rf rear ra) s))) ))))

This works, but is awkward.

CS 798 Fall 2009                  09: Declarative languages            41   CS 798 Fall 2009              09: Declarative languages     42




(define (append-r front rear all)                                            (define (lookup∗ var s)
   (disj (conj∗ (== front empty) (== rear all))                                (define v (lookup var s))
            (local [(define ff (var ’ff))                                       (cond
                      (define rf (var ’rf))                                         [(var? v) v]
                      (define ra (var ’ra))]                                        [(cons? v)
                   (conj∗                                                               (cons (lookup∗ (car v) s)
                     (cons-r ff rf front)                                                       (lookup∗ (cdr v) s))]
                     (cons-r ff ra all)                                            [else v]))
                     (append-r rf rear ra)))))
                                                                            (define (run2 g)
                                                                               (map (lambda (s) (lookup∗ vq s)) (g empty-subst)))


CS 798 Fall 2009                  09: Declarative languages            43   CS 798 Fall 2009              09: Declarative languages     44
(test (run2 (append-r ’(1 2 3) vq ’(1 2 3 4 5)))               Using a lazy language such as Haskell eliminates the need
         ’((4 5)))                                             for the “lambda-wrapping”.
                                                               It also permits computation of elements of a list of
(test (run2 (append-r vx vq ’(1 2 3 4 5)))
                                                               successes on demand, instead of in advance.
         ’((1 2 3 4 5) (2 3 4 5) (3 4 5) (4 5) (5) ()))
                                                               Friedman, Byrd, and Kiselyov develop these ideas further
                                                               in Scheme, in their Kanren system.
(test (run2 (conj∗ (append-r vx vy ’(1 2 3 4 5))
                        (== vq (list vx vy))))                 Modern successors to Prolog include the Oz and Mercury

         ’((() (1 2 3 4 5)) ((1) (2 3 4 5))                    languages.

            ((1 2) (3 4 5)) ((1 2 3) (4 5))
            ((1 2 3 4) (5)) ((1 2 3 4 5) ())))


CS 798 Fall 2009              09: Declarative languages   45   CS 798 Fall 2009       09: Declarative languages       46

				
DOCUMENT INFO