A categorical programming language
Revision | dced8f9d1557e94b91e62630f19b29d4c70d0e33 (tree) |
---|---|
Time | 2022-12-28 12:10:43 |
Author | Corbin <cds@corb...> |
Commiter | Corbin |
Rewrite djinn core.
Now we pull apart types first, synthesizing expressions from the pieces.
The results are quite good; with only a few tactics, we are able to
synthesize all of the existing testcases, including the three that used
to fail.
@@ -6,7 +6,7 @@ import sys | ||
6 | 6 | movelist = sys.argv[-2] |
7 | 7 | jelly = sys.argv[-1] |
8 | 8 | timeout = 5 |
9 | -difficulty = 15 | |
9 | +difficulty = 5 | |
10 | 10 | |
11 | 11 | def jellify(expr): |
12 | 12 | return subprocess.run([jelly], input=expr.encode("utf-8"), |
@@ -1,32 +1,12 @@ | ||
1 | -(module cammyo (cammy° normal-form°) | |
1 | +(module cammyo (cammy° cammy-prim° cammy-synth°) | |
2 | 2 | (import scheme) |
3 | + (import (chicken pretty-print)) | |
3 | 4 | (import (mini-kanren)) |
4 | 5 | |
5 | - ; Require that a Cammy expression is in a basic normal form. | |
6 | - (define (normal-form° expr) | |
7 | - (fresh (x y) | |
8 | - ; NF: disallow (comp id ...) and (comp ... id) | |
9 | - (=/= expr `(comp id ,x)) | |
10 | - (=/= expr `(comp ,x id)) | |
11 | - ; NF: disallow (comp ... ignore) | |
12 | - (=/= expr `(comp ,x ignore)) | |
13 | - ; NF: disallow (comp left/right (case ...)) | |
14 | - (=/= expr `(comp left (case ,x ,y))) | |
15 | - (=/= expr `(comp right (case ,x ,y))) | |
16 | - ; NF: disallow (comp (pair ...) fst/snd) | |
17 | - (=/= expr `(comp (pair ,x ,y) fst)) | |
18 | - (=/= expr `(comp (pair ,x ,y) snd)) | |
19 | - ; NF: disallow (curry (uncurry ...)) and (uncurry (curry ...)) | |
20 | - (=/= expr `(curry (uncurry ,x))) | |
21 | - (=/= expr `(uncurry (curry ,x))) | |
22 | - )) | |
23 | - | |
24 | - ; Expr <=> Ty × Ty | |
25 | - ; Relate a Cammy expression to its input and output types. | |
26 | - ; cammy° is functional; expr fixes s and t together. | |
27 | - (define (cammy° expr s t) | |
28 | - (fresh () | |
29 | - ; (project (expr s t) (begin (pp `(enter ,expr ,s ,t)) succeed)) | |
6 | + ; Relate a monomorphic Cammy primitive to its input and output types. | |
7 | + ; This relation is just a table. | |
8 | + ; Expr <-> Ty × Ty | |
9 | + (define (cammy-prim° expr s t) | |
30 | 10 | (conde |
31 | 11 | ; Boolean algebra |
32 | 12 | ((== expr 't) (== s '1) (== t '2)) |
@@ -52,6 +32,20 @@ | ||
52 | 32 | ((== expr 'f-atan2) (== s '(pair F F)) (== t 'F)) |
53 | 33 | ((== expr 'f-exp) (== s 'F) (== t 'F)) |
54 | 34 | ((== expr 'f-log1p) (== s 'F) (== t '(sum F 1))) |
35 | + ; NNO | |
36 | + ((== expr 'succ) (== s 'N) (== t 'N)) | |
37 | + ((== expr 'zero) (== s '1) (== t 'N)) | |
38 | + ((== expr 'n-pred-maybe) (== s 'N) (== t '(sum N 1))) | |
39 | + ((== expr 'n-add) (== s '(pair N N)) (== t 'N)) | |
40 | + )) | |
41 | + | |
42 | + ; Relate a Cammy expression to its input and output types. | |
43 | + ; cammy° is functional; expr fixes s and t together. | |
44 | + ; Expr <-> Ty × Ty | |
45 | + (define (cammy° expr s t) | |
46 | + (conde | |
47 | + ; Primitives | |
48 | + ((cammy-prim° expr s t)) | |
55 | 49 | ; Cartesian Closed Categories |
56 | 50 | ((fresh (f y z) (== expr `(curry ,f)) |
57 | 51 | (== t `(hom ,y ,z)) (cammy° f `(pair ,s ,y) z))) |
@@ -72,24 +66,82 @@ | ||
72 | 66 | ((fresh (f g s1 s2) (== expr `(case ,f ,g)) |
73 | 67 | (== s `(sum ,s1 ,s2)) (cammy° f s1 t) (cammy° g s2 t))) |
74 | 68 | ; NNO |
75 | - ((== expr 'succ) (== s 'N) (== t 'N)) | |
76 | - ((== expr 'zero) (== s '1) (== t 'N)) | |
77 | 69 | ((fresh (x f) (== expr `(pr ,x ,f)) |
78 | 70 | (== s 'N) (cammy° x '1 t) (cammy° f t t))) |
79 | - ((== expr 'n-pred-maybe) (== s 'N) (== t '(sum N 1))) | |
80 | - ((== expr 'n-add) (== s '(pair N N)) (== t 'N)) | |
81 | 71 | ; Free Monoids |
82 | 72 | ((== expr 'cons) (fresh (l) (== s `(pair ,l ,t)) (== t `(list ,l)))) |
83 | 73 | ((== expr 'nil) (== s '1) (fresh (l) (== t `(list ,l)))) |
84 | 74 | ((fresh (x f l) (== expr `(fold ,x ,f)) |
85 | 75 | (== s `(list ,l)) (cammy° x '1 t) (cammy° f `(pair ,l ,t) t))) |
86 | 76 | ; Terminal Object |
87 | - ((== expr 'ignore) (== t '1)) | |
77 | + ; NB: ignore : X -> 1 but only when X ≠ 1, because otherwise it is the unique | |
78 | + ; arrow, id : 1 -> 1 | |
79 | + ((== expr 'ignore) (=/= s '1) (== t '1)) | |
88 | 80 | ; Categories |
89 | 81 | ((== expr 'id) (== s t)) |
90 | - ((fresh (f g y) (== expr `(comp ,f ,g)) | |
91 | - (cammy° f s y) (cammy° g y t))) | |
92 | - ) | |
93 | - ; (project (expr s t) (begin (pp `(exit ,expr ,s ,t)) succeed)) | |
94 | - )) | |
82 | + ((fresh (f g) (== expr `(comp ,f ,g)) | |
83 | + (fresh (y) (cammy° f s y) (cammy° g y t)))) | |
84 | + )) | |
85 | + | |
86 | + ; Syntactic composition enforcing normal form. This is just a synthesis helper. | |
87 | + ; Cammy × Cammy <-> Cammy | |
88 | + (define (comp-s° f g fg) (=/= f 'id) (=/= g 'id) (== fg `(comp ,f ,g))) | |
89 | + | |
90 | + ; Like cammy°, but suitable for type-directed term synthesis. All expressions | |
91 | + ; are normalized, and logical clauses are ordered to try some expert strategies. | |
92 | + ; Cammy <-> Ty × Ty | |
93 | + (define (cammy-synth° expr s t) | |
94 | + ; s, t, u, v are types | |
95 | + ; f, g are Cammy expressions | |
96 | + (fresh (f g u v) | |
97 | + ; (project (expr s t) (begin (pp `(synth? ,expr : ,s -> ,t)) succeed)) | |
98 | + (conde | |
99 | + ; Try primitives first. | |
100 | + ((cammy-prim° expr s t)) | |
101 | + ; Is it the unique identity arrow? | |
102 | + ((== s t) (== expr 'id)) | |
103 | + ; Is it any arrow into 1? | |
104 | + ((== t '1) (=/= s '1) (== expr 'ignore)) | |
105 | + ; Is it an NT? | |
106 | + ((== `(pair ,s ,s) t) (== expr 'dup)) | |
107 | + ((== s `(pair ,t ,u)) (== expr 'fst)) | |
108 | + ((== s `(pair ,u ,t)) (== expr 'snd)) | |
109 | + ((== s `(pair ,u ,v)) (== t `(pair ,v ,u)) (== expr 'swap)) | |
110 | + ((== s `(pair (hom ,u ,t) ,u)) (== expr 'app)) | |
111 | + ((== `(sum ,s ,u) t) (== expr 'left)) | |
112 | + ((== `(sum ,u ,s) t) (== expr 'right)) | |
113 | + ; Is it a tuple lookup? | |
114 | + ((== s `(pair ,u ,v)) | |
115 | + (conde | |
116 | + ((comp-s° 'fst f expr) (cammy-synth° f u t)) | |
117 | + ((comp-s° 'snd f expr) (cammy-synth° f v t)))) | |
118 | + ; Is it a case analysis? | |
119 | + ((== s `(sum ,u ,v)) (cammy-synth° f u t) (cammy-synth° g v t) (== expr `(case ,f ,g))) | |
120 | + ; Is it a tagging or return value? | |
121 | + ((== t `(sum ,u ,v)) | |
122 | + (conde | |
123 | + ((comp-s° f 'left expr) (cammy-synth° f s u)) | |
124 | + ((comp-s° f 'right expr) (cammy-synth° f s v)))) | |
125 | + ; Is it a graph? | |
126 | + ; NB: Want to disallow (pair id id) | |
127 | + ((== `(pair ,s ,u) t) (=/= f 'id) (cammy-synth° f s u) (== expr `(pair id ,f))) | |
128 | + ((== `(pair ,u ,s) t) (=/= f 'id) (cammy-synth° f s u) (== expr `(pair ,f id))) | |
129 | + ; Is it a curry? | |
130 | + ((== t `(hom ,u ,v)) (cammy-synth° f `(pair ,s ,u) v) (== expr `(curry ,f))) | |
131 | + ; Is it an application? | |
132 | + ((cammy-synth° f s `(hom ,u ,t)) (cammy-synth° g s u) (comp-s° `(pair ,f ,g) 'app expr)) | |
133 | + ; Is it a pair-builder? | |
134 | + ((== `(pair ,u ,v) t) (cammy-synth° f s u) (cammy-synth° g s v) (== expr `(pair ,f ,g))) | |
135 | + ; Does it result in a list? | |
136 | + ((== t `(list ,u)) | |
137 | + (conde | |
138 | + ((== s '1) (== expr 'nil)) | |
139 | + ((== s `(pair ,u (list ,u))) (== expr 'cons)))) | |
140 | + ; Is it primitive recursive? | |
141 | + ((== s 'N) (cammy-synth° f '1 t) (=/= g 'id) (cammy-synth° g t t) (== expr `(pr ,f ,g))) | |
142 | + ; Is it a composition? | |
143 | + ; ((comp-s° f g expr) (cammy-synth° f s u) (cammy-synth° g u t)) | |
144 | + ) | |
145 | + ; (project (expr s t) (begin (pp `(found: ,expr : ,s -> ,t)) succeed)) | |
146 | + )) | |
95 | 147 | ) |
@@ -5,13 +5,13 @@ | ||
5 | 5 | |
6 | 6 | (import (cammyo)) |
7 | 7 | |
8 | -(define (last xs) | |
9 | - (if (null? (cdr xs)) (car xs) (last (cdr xs)))) | |
8 | +(define (print-each xs) | |
9 | + (if (null? xs) #f | |
10 | + (begin (display (car xs)) (newline) (print-each (cdr xs))))) | |
10 | 11 | |
11 | 12 | (define (djinn x s t) |
12 | - (last (run x (q) (normal-form° q) (cammy° q s t)))) | |
13 | + (print-each (run x (q) (cammy-synth° q s t)))) | |
13 | 14 | |
14 | 15 | (match (map (lambda (s) (read (open-input-string s))) |
15 | 16 | (command-line-arguments)) |
16 | - [(in out (? number? count)) (begin (display (djinn count in out)) | |
17 | - (newline))]) | |
17 | + [(in out (? number? count)) (djinn count in out)]) |