2 First Edition
| (require typed/miniKanren/trs1) | package: miniKanren-typed |
This module implements the miniKanren language used in the first edition of The Reasoned Schemer (Friedman, Byrd, and Kiselyov, MIT Press, 2005). It keeps the operators of that edition, including the interleaving forms condi and alli and the committed-choice forms conda and condu. The types make the structure of the implementation visible: terms, substitutions, goals, and the lazy streams that carry answers all have names, and every internal operator is exported so that it can be inspected and tested in isolation.
2.1 Implementation Basis
The implementation follows the one described in the first edition: unification and reification are those of Chapter 9 (“Under the Hood”), and the stream operators and goal constructors are those of the appendix “Connecting the Wires”. The second edition is a different language (see Second Edition and Comparing the Editions).
The differences from the first edition are:
Stream representation. The book represents “exactly one answer” by the answer itself and “an answer followed by more” by a pair whose cdr is a procedure. That union cannot be discriminated by occurrence typing for an arbitrary answer type, so here a single answer is wrapped in an immutable box and the empty stream is '(). See Streamof.
Occurs check. The book’s ext-s never checks for circularity; a separate checked variant is introduced in Chapter 9. Here ext-s always performs the occurs check (skipping it when the value contains no variables), so == is the checked unification and there is no separate checked variant.
Spelling. succeed and fail stand for the book’s #s and #u; λG and λF are typed versions of the book’s goal and thunk lambdas. Superscripts become suffixes: conde, condi, alli, ife, and so on.
Omissions. The helper forms that the book introduces in passing to explain a point (such as the bounded lambda of Chapter 5 and project of Chapter 9) are not part of the library.
Throughout this section, “Chapter n” refers to the first edition.
2.2 Types
type
type
type
type
type constructor
(Streamof a)
(∪ Null (Boxof a) (Pair a (→ (Streamof a))))
A stream is empty, contains exactly one answer, or contains one answer followed by a suspended computation (a thunk) that produces the rest. The middle case is the book’s unit; the last is its choice.
type
2.3 Running Programs
Unassociated variables in an answer are reified as the symbols '.0, '.1, and so on, in order of first appearance.
2.4 Goals
> (run* (q) (fresh (x) (== q (cons x 2)) (== x 1))) - : (Listof Term)
'((1 . 2))
> (run* (q) (== q (list q))) - : (Listof Term)
'()
syntax
(fresh (x ...) goal ...)
all explores the answers of the first goal one at a time: for the first answer it exhausts the rest of the conjunction before moving to the second answer. alli (“all, interleaved”) alternates between the answers produced for different answers of the earlier goals, so a later answer of the first goal is reached even when an earlier one leads to infinitely many results. (all, Chapter 6; alli, Chapter 6.)
In the examples below, the first goal chooses 'L or 'R, and counto then relates n to a list of 'Is of any length. The first choice already has infinitely many answers, so all never reaches 'R; alli alternates between the two choices.
> (: counto (→ Term Goal))
> (define (counto n) (conde [(== n '())] [(fresh (m) (== n (cons 'I m)) (counto m))]))
> (run 6 (q) (fresh (n side) (all (conde [(== side 'L)] [(== side 'R)]) (counto n)) (== q (cons side n)))) - : (Listof Term)
'((L) (L I) (L I I) (L I I I) (L I I I I) (L I I I I I))
> (run 6 (q) (fresh (n side) (alli (conde [(== side 'L)] [(== side 'R)]) (counto n)) (== q (cons side n)))) - : (Listof Term)
'((L) (R) (L I) (R I) (L I I) (R I I))
conde is depth-first: it produces all the answers of the first clause before any answer of the second, so a clause with infinitely many answers hides every clause after it. condi (“cond, interleaved”) alternates between the clauses. (conde, Chapter 1; condi, Chapter 6.)
> (: repeato (→ Term Term Goal))
> (define (repeato x l) (conde [(== l '())] [(fresh (d) (== l (cons x d)) (repeato x d))])) > (run 6 (q) (conde [(repeato 'a q)] [(repeato 'b q)])) - : (Listof Term)
'(() (a) (a a) (a a a) (a a a a) (a a a a a))
> (run 6 (q) (condi [(repeato 'a q)] [(repeato 'b q)])) - : (Listof Term)
'(() () (a) (b) (a a) (b b))
In conda (“soft cut”), every answer of the chosen test is passed to the rest of its clause. In condu (“committed choice”), only the first answer of the test is kept. Neither form is relational in general: the result can depend on how much is already known about the variables. (Chapter 10.)
2.5 Substitutions and Unification
These are the operations of Chapter 9.
value
procedure
s : Substitution
procedure
(ext-s x v s) → Substitution
x : Var v : Data s : Substitution (ext-s x v s) → (Option Substitution) x : Var v : Term s : Substitution
> (define x (var 'x)) > (ext-s x '(1 2) empty-s) - : Substitution
(list (list (var 'x) 1 2))
> (ext-s x (list 1 x) empty-s) - : (U False Substitution)
#f
procedure
x : Var v : Term s : Substitution
procedure
v : Term s : Substitution
> (define x (var 'x)) > (define y (var 'y)) > (walk x (list (cons x y) (cons y 'end))) - : Term
'end
> (walk (cons x x) (list (cons x 1))) - : Term
(cons (var 'x) (var 'x))
procedure
v : Term s : Substitution
procedure
(unify u v s) → (Option Substitution)
u : Term v : Term s : Substitution
> (define x (var 'x)) > (define y (var 'y)) > (unify (cons x 2) (cons 1 y) empty-s) - : (U False Substitution)
(list (cons (var 'y) 2) (cons (var 'x) 1))
> (unify (cons x 2) (cons 1 3) empty-s) - : (U False Substitution)
#f
procedure
(reify-name n) → Symbol
n : Index
procedure
(reify-s v s) → Substitution
v : Term s : Substitution
procedure
v : Term
2.6 Streams
These are the operators of the appendix “Connecting the Wires”. They are exported so that goals can be written and examined at the level of streams.
syntax
(case∞ e [() on-zero] [(a) on-one] [(a f) on-choice])