#lang scribble/doc @(require scribble/manual scribble/eval scribble/basic scribble/bnf (planet cce/scheme:4:1/planet) (for-label (planet dherman/pprint:4) scheme/base scheme/contract "../main.ss") "utils.ss") @title[#:tag "top"]{@bold{Datalog} for PLT Scheme} @author[(author+email "Jay McCarthy" "jay@plt-scheme.org")] This package contains a lightweight deductive database system. Queries and database updates are expressed using @link["http://en.wikipedia.org/wiki/Datalog"]{Datalog}---a declarative logic language in which each formula is a function-free Horn clause, and every variable in the head of a clause must appear in the body of the clause. The use of Datalog syntax and an implementation based on tabling intermediate results ensures that all queries terminate. @table-of-contents[] @section{Datalog for PLT Scheme} @subsection{Getting Started} The easiest way to get started using Datalog for PLT Scheme is with the main module: @defmodule/this-package[] This module provides everything in the entire package. Subsequent sections of this manual describe the functionality of the individual libraries included, which can also be required individually. @examples[#:eval the-eval (define example-program #<syntax]. Equivalent to @schemeblock[ (or/c syntax? false/c (list/c any/c (or/c exact-positive-integer? #f) (or/c exact-nonnegative-integer? #f) (or/c exact-nonnegative-integer? #f) (or/c exact-positive-integer? #f))) ] } @defthing[datum/c contract?]{ Contract for @deftech{datum}s. Equivalent to @scheme[(or/c string? symbol?)]. } @defproc[(datum-equal? [d1 datum/c] [d2 datum/c]) boolean?]{ Equivalent to @scheme[(equal? d1 d2)]. @examples[#:eval the-eval (datum-equal? 'sym1 'sym2) (datum-equal? 'sym1 'sym1) (datum-equal? "str1" "str2") (datum-equal? "str1" "str1")] } @defstruct[variable ([srcloc srcloc/c] [sym symbol?])]{ A logic variable in Datalog. (This structure does not enforce the requirements for what characters variables can contain, so if you print one out, it might not be parseable, but it will be executeable.) } @defproc[(variable-equal? [v1 variable?] [v2 variable?]) boolean?]{ Equivalent to @scheme[(equal? v1 v2)] modulo source location. @examples[#:eval the-eval (variable-equal? (make-variable #f 'sym) (make-variable #'sym 'sym)) (variable-equal? (make-variable #f 'sym1) (make-variable #f 'sym2))] } @defstruct[constant ([srcloc srcloc/c] [datum datum/c])]{ A constant in Datalog. (This structure does not enforce the requirements for what characters constants can contain, so if you print one out, it might not be parseable, but it will be executeable.) } @defproc[(constant-equal? [c1 constant?] [c2 constant?]) boolean?]{ Equivalent to @scheme[(equal? c1 c2)] modulo source location. @examples[#:eval the-eval (constant-equal? (make-constant #f 'sym) (make-constant #'sym 'sym)) (constant-equal? (make-constant #f 'sym) (make-constant #f "str"))] } @defthing[term/c contract?]{ Contract for @deftech{term}s. Equivalent to @scheme[(or/c variable? constant?)]. } @defproc[(term-equal? [t1 term/c] [t2 term/c]) boolean?]{ Equivalent to @scheme[(equal? t1 t2)] modulo source location. @examples[#:eval the-eval (term-equal? (make-constant #f 'sym) (make-constant #'sym 'sym)) (term-equal? (make-constant #f 'sym) (make-constant #f "str"))] } @defstruct[literal ([srcloc srcloc/c] [predicate datum/c] [terms (listof term/c)])]{ A literal in Datalog. } @defproc[(literal-equal? [l1 literal?] [l2 literal?]) boolean?]{ Equivalent to @scheme[(equal? l1 l2)] modulo source location. @examples[#:eval the-eval (literal-equal? (make-literal #f 'ancestor (list)) (make-literal #'ancestor 'ancestor (list))) (literal-equal? (make-literal #f 'ancestor (list)) (make-literal #f 'parent (list))) (literal-equal? (make-literal #f 'ancestor (list)) (make-literal #f 'ancestor (list (make-constant #f 'jack))))] } @defstruct[clause ([srcloc srcloc/c] [head literal?] [body (listof literal?)])]{ A Datalog clause. } @defproc[(clause-equal? [c1 clause?] [c2 clause?]) boolean?]{ Equivalent to @scheme[(equal? c1 c2)] modulo source location. @examples[#:eval the-eval (clause-equal? (make-clause #f (make-literal #f 'ancestor (list)) (list)) (make-clause #'clause (make-literal #f 'ancestor (list)) (list))) (clause-equal? (make-clause #f (make-literal #f 'ancestor (list)) (list)) (make-clause #f (make-literal #f 'parent (list)) (list)))] } @defstruct[assertion ([srcloc srcloc/c] [clause clause?])]{ A Datalog assertion. } @defstruct[retraction ([srcloc srcloc/c] [clause clause?])]{ A Datalog retraction. } @defstruct[query ([srcloc srcloc/c] [clause clause?])]{ A Datalog query. } @defthing[statement/c contract?]{ Contract for @deftech{statement}s. Equivalent to @scheme[(or/c assertion? retraction? query?)]. } @defthing[program/c contract?]{ Contract for @deftech{program}s. Equivalent to @scheme[(listof statement/c)]. } @section{Lexing and Parsing} This library provides facilities for parsing Datalog source. It can be required via: @defmodule/this-package[parse] @subsection{Datalog Syntax} In Datalog input, whitespace characters are ignored except when they separate adjacent tokens or when they occur in strings. Comments are also considered to be whitespace. The character @litchar["%"] introduces a comment, which extends to the next line break. Comments do not occur inside strings. A variable is a sequence of Latin capital and small letters, digits, and the underscore character. A variable must begin with a Latin capital letter. An identifier is a sequence of printing characters that does not contain any of the following characters: @litchar["("], @litchar["`"], @litchar["'"], @litchar[")"], @litchar["="], @litchar[":"], @litchar["."], @litchar["~"], @litchar["?"], @litchar["\""], @litchar["%"], and space. An identifier must not begin with a Latin capital letter. Note that the characters that start punctuation are forbidden in identifiers, but the hyphen character is allowed. A string is a sequence of characters enclosed in double quotes. Characters other than double quote, newline, and backslash may be directly included in a string. The remaining characters may be specified using escape characters, @litchar["\\\""], @litchar["\\\n"], and @litchar["\\\\"] respectively. A literal, is a predicate symbol followed by an optional parenthesized list of comma separated terms. A predicate symbol is either an identifier or a string. A term is either a variable or a constant. As with predicate symbols, a constant is either an identifier or a string. As a special case, two terms separated by @litchar["="] is a literal for the equality predicate. The following are literals: @verbatim[#:indent 4 #<term [stx syntax?]) term/c]{ Parses @scheme[stx] as a @tech{term}. } @defproc[(stx->literal [stx syntax?]) literal?]{ Parses @scheme[stx] as a @scheme[literal]. } @defproc[(stx->clause [stx syntax?]) clause?]{ Parses @scheme[stx] as a @scheme[clause]. } @defproc[(stx->statement [stx syntax?]) statement/c]{ Parses @scheme[stx] as a @tech{statement}. } @defproc[(stx->program [stx syntax?]) program/c]{ Parses @scheme[stx] as a @tech{program}. } @defproc[(sexp->term [sexp sexpr?]) term/c]{@scheme[stx->term] composed with @scheme[datum->syntax].} @defproc[(sexp->literal [sexp sexpr?]) literal?]{@scheme[stx->literal] composed with @scheme[datum->syntax].} @defproc[(sexp->clause [sexp sexpr?]) clause?]{@scheme[stx->clause] composed with @scheme[datum->syntax].} @defproc[(sexp->statement [sexp sexpr?]) statement/c]{@scheme[stx->statement] composed with @scheme[datum->syntax].} @defproc[(sexp->program [sexp sexpr?]) program/c]{@scheme[stx->program] composed with @scheme[datum->syntax].} @section{Pretty-Printing} This library provides facilities for pretty-printing Datalog source. It can be required via: @defmodule/this-package[pretty] This library depends on the @tt{pprint} PLaneT package, which can be required via: @schemeblock[(require (planet dherman/pprint:4))] See the documentation for @tt{pprint} for information on how to use it. @defproc[(format-datum [d datum/c]) doc?]{ Formats a @tech{datum}. @examples[#:eval the-eval (pretty-print (format-datum 'sym)) (pretty-print (format-datum "str"))] } @defproc[(format-variable [v variable?]) doc?]{ Formats a @scheme[variable]. @examples[#:eval the-eval (pretty-print (format-variable (make-variable #f 'Ancestor)))] } @defproc[(format-constant [c constant?]) doc?]{ Formats a @scheme[constant]. @examples[#:eval the-eval (pretty-print (format-constant (make-constant #f 'joseph))) (pretty-print (format-constant (make-constant #f "whom")))] } @defproc[(format-term [t term/c]) doc?]{ Formats a @tech{term}. @examples[#:eval the-eval (pretty-print (format-term (make-variable #f 'Ancestor))) (pretty-print (format-term (make-constant #f 'joseph))) (pretty-print (format-term (make-constant #f "whom")))] } @defproc[(format-literal [l literal?]) doc?]{ Formats a @scheme[literal]. @examples[#:eval the-eval (pretty-print (format-literal (make-literal #f 'true (list)))) (pretty-print (format-literal (make-literal #f 'ancestor (list (make-variable #f 'A) (make-constant #f 'jay))))) (pretty-print (format-literal (make-literal #f '= (list (make-constant #f 'joseph) (make-constant #f 'jay)))))] } @defproc[(format-literals [ls (listof literal?)]) doc?]{ Formats a list of @scheme[literal]s as @scheme[assertion]s for formatting @scheme[prove] results. @examples[#:eval the-eval (pretty-print (format-literals (list (make-literal #f 'true (list)) (make-literal #f 'ancestor (list (make-constant #f 'joseph) (make-constant #f 'jay))) (make-literal #f '= (list (make-constant #f 'joseph) (make-constant #f 'jay))))))] } @defproc[(format-clause [c clause?]) doc?]{ Formats a @scheme[clause]. @examples[#:eval the-eval (pretty-print (format-clause (make-clause #f (make-literal #f 'ancestor (list (make-constant #f 'joseph) (make-constant #f 'jay))) (list)))) (pretty-print (format-clause (make-clause #f (make-literal #f 'ancestor (list (make-constant #f 'A) (make-constant #f 'B))) (list (make-literal #f 'parent (list (make-constant #f 'A) (make-constant #f 'B))))))) (pretty-print (format-clause (make-clause #f (make-literal #f 'ancestor (list (make-constant #f 'A) (make-constant #f 'B))) (list (make-literal #f 'parent (list (make-constant #f 'A) (make-constant #f 'C))) (make-literal #f 'ancestor (list (make-constant #f 'C) (make-constant #f 'B)))))))] } @defproc[(format-assertion [a assertion?]) doc?]{ Formats a @scheme[assertion]. @examples[#:eval the-eval (pretty-print (format-assertion (make-assertion #f (make-clause #f (make-literal #f 'ancestor (list (make-constant #f 'joseph) (make-constant #f 'jay))) (list)))))] } @defproc[(format-retraction [r retraction?]) doc?]{ Formats a @scheme[retraction]. @examples[#:eval the-eval (pretty-print (format-retraction (make-retraction #f (make-clause #f (make-literal #f 'ancestor (list (make-constant #f 'joseph) (make-constant #f 'jay))) (list)))))] } @defproc[(format-query [q query?]) doc?]{ Formats a @scheme[query]. @examples[#:eval the-eval (pretty-print (format-query (make-query #f (make-literal #f 'ancestor (list (make-constant #f 'joseph) (make-constant #f 'jay))))))] } @defproc[(format-statement [s statement/c]) doc?]{ Formats a @tech{statement}. @examples[#:eval the-eval (pretty-print (format-statement (make-query #f (make-literal #f 'ancestor (list (make-constant #f 'joseph) (make-constant #f 'jay))))))] } @defproc[(format-program [p program/c]) doc?]{ Formats a @tech{program}. @examples[#:eval the-eval (pretty-print (format-program (list (make-assertion #f (make-clause #f (make-literal #f 'ancestor (list (make-constant #f 'joseph) (make-constant #f 'jay))) (list))) (make-query #f (make-literal #f 'ancestor (list (make-constant #f 'joseph) (make-constant #f 'jay)))))))] } @section{Runtime System} This library implements the Datalog runtime system. It can be required via: @defmodule/this-package[runtime] @defthing[theory/c contract?]{ A contract for @deftech{theories}. } @defthing[immutable-theory/c contract?]{ A contract for immutable @tech{theories}. } @defthing[mutable-theory/c contract?]{ A contract for mutable @tech{theories}. } @defproc[(make-mutable-theory) mutable-theory/c]{ Constructs a mutable @tech{theory}. } @defproc[(make-immutable-theory) immutable-theory/c]{ Constructs a immutable @tech{theory}. } @defproc[(safe-clause? [c clause?]) boolean?]{ Determines if a @scheme[clause] is safe. A @scheme[clause] is safe if every @scheme[variable] in its head occurs in some @scheme[literal] in its body. @examples[#:eval the-eval (safe-clause? (parse-clause (open-input-string "ancestor(joseph,jay)"))) (safe-clause? (parse-clause (open-input-string "ancestor(A,B) :- parent(A,B)"))) (safe-clause? (parse-clause (open-input-string "ancestor(A,B) :- parent(A,jay)")))] } @defproc[(assume [thy immutable-theory/c] [c safe-clause?]) immutable-theory/c]{ Adds @scheme[c] to @scheme[thy] in a persistent way. } @defproc[(retract [thy immutable-theory/c] [c clause?]) immutable-theory/c]{ Removes @scheme[c] from @scheme[thy] in a persistent way. } @defproc[(assume! [thy mutable-theory/c] [c safe-clause?]) mutable-theory/c]{ Adds @scheme[c] to @scheme[thy]. } @defproc[(retract! [thy mutable-theory/c] [c clause?]) mutable-theory/c]{ Removes @scheme[c] from @scheme[thy]. } @defproc[(prove [thy theory/c] [l literal?]) (listof literal?)]{ Attempts to prove @scheme[l] using the @tech{theory} @scheme[thy], returning all the results of the query. @examples[#:eval the-eval (pretty-print (format-literals (prove (assume (make-immutable-theory) (parse-clause (open-input-string "parent(joseph1,joseph2)"))) (parse-literal (open-input-string "parent(joseph1,joseph2)"))))) (pretty-print (format-literals (prove (retract (assume (make-immutable-theory) (parse-clause (open-input-string "parent(joseph1,joseph2)"))) (parse-clause (open-input-string "parent(joseph1,joseph2)"))) (parse-literal (open-input-string "parent(joseph1,joseph2)"))))) (pretty-print (format-literals (prove (assume (make-immutable-theory) (parse-clause (open-input-string "parent(joseph1,joseph2)"))) (parse-literal (open-input-string "parent(A,B)")))))] } @section{Evaluation} This library provides facilities for evaluating Datalog. It can be required via: @defmodule/this-package[eval] @defthing[current-theory (parameter/c mutable-theory/c)]{ The @tech{theory} used by @scheme[eval-program] and @scheme[eval-stmt]. } @defproc[(eval-program [p program/c]) void]{ Evaluates @scheme[p] using @scheme[(current-theory)] as the @tech{theory}, printing query answers as it goes. This will raise a syntax error if given an @scheme[assertion] of a @scheme[clause] that is not a @scheme[safe-clause?]. @examples[#:eval the-eval (parameterize ([current-theory (make-mutable-theory)]) (eval-program (parse-program (open-input-string (string-append "edge(a, b). edge(b, c). edge(c, d). edge(d, a)." "path(X, Y) :- edge(X, Y)." "path(X, Y) :- edge(X, Z), path(Z, Y)." "path(X, Y)?"))))) (eval-program (parse-program (open-input-string "path(X, Y) :- edge(X, a).")))] } @defproc[(eval-statement [s statement/c]) (or/c void (listof literal?))]{ Evaluates @scheme[s] using @scheme[(current-theory)] as the @tech{theory}. This will raise a syntax error if given an @scheme[assertion] of a @scheme[clause] that is not a @scheme[safe-clause?]. @examples[#:eval the-eval (parameterize ([current-theory (make-mutable-theory)]) (eval-statement (parse-statement (open-input-string "edge(a, b)."))) (eval-statement (parse-statement (open-input-string "path(X, Y) :- edge(X, Y)."))) (eval-statement (parse-statement (open-input-string "path(X, Y)?")))) (eval-statement (parse-statement (open-input-string "path(X, Y) :- edge(X, a).")))] } @defproc[(eval-program/fresh [p program/c]) immutable-theory/c]{ Evaluates @scheme[p] in a fresh @tech{theory} and returns the final @tech{theory}, printing query answers as it goes. This will raise a syntax error if given an @scheme[assertion] of a @scheme[clause] that is not a @scheme[safe-clause?]. @examples[#:eval the-eval (void (eval-program/fresh (parse-program (open-input-string (string-append "edge(a, b). edge(b, c). edge(c, d). edge(d, a)." "path(X, Y) :- edge(X, Y)." "path(X, Y) :- edge(X, Z), path(Z, Y)." "path(X, Y)?"))))) (eval-program/fresh (parse-program (open-input-string "path(X, Y) :- edge(X, a).")))] } @index-section[] @section{Acknowledgments} This package is based on Dave Herman's @schememodname[(planet dherman/javascript)] library and John Ramsdell's @link["http://www.ccs.neu.edu/home/ramsdell/tools/datalog/datalog.html"]{Datalog library}. The package uses the tabled logic programming algorithm described in @link["http://scholar.google.com/scholar?q=author:%22Chen%22+intitle:%22Efficient+top-down+computation+of+queries+under+the+...%22+&oi=scholarr"]{Efficient Top-Down Computation of Queries under the Well-Founded Semantics} by W. Chen, T. Swift, and D. S. Warren. Another important reference is @link["http://portal.acm.org/citation.cfm?id=227597"]{Tabled Evaluation with Delaying for General Logic Programs} by W. Chen and D. S. Warren. Datalog is described in @link["http://doi.ieeecomputersociety.org/10.1109/69.43410"]{What You Always Wanted to Know About Datalog (And Never Dared to Ask)} by Stefano Ceri, Georg Gottlob, and Letizia Tanca.