A categorical programming language
修订版 | 876b22629fa5fb390cc8631027af540dc08c734b (tree) |
---|---|
时间 | 2022-03-09 10:16:42 |
作者 | Corbin <cds@corb...> |
Commiter | Corbin |
Clean up pretty-printing for types.
@@ -39,12 +39,18 @@ class Atom(SExp): | ||
39 | 39 | def occurs(self, index): |
40 | 40 | return False |
41 | 41 | |
42 | - def extractType(self, extractor): | |
42 | + def extractType(self, extractor, formatter, outerPrecedence): | |
43 | 43 | return self.symbol |
44 | 44 | |
45 | 45 | def buildArrow(self): |
46 | 46 | return buildUnary(self.symbol) |
47 | 47 | |
48 | +# Precedence levels for operator-style notation. | |
49 | +PRECEDENCE = { | |
50 | + "hom": 3, | |
51 | + "pair": 2, | |
52 | + "sum": 1, | |
53 | +} | |
48 | 54 | |
49 | 55 | class Functor(SExp): |
50 | 56 | "A list of S-expressions with a distinguished head." |
@@ -77,18 +83,26 @@ class Functor(SExp): | ||
77 | 83 | return True |
78 | 84 | return False |
79 | 85 | |
80 | - def extractType(self, extractor): | |
81 | - args = [extractor.extract(unhole(arg)) for arg in self.arguments] | |
86 | + def extractType(self, extractor, formatter, outerPrecedence): | |
87 | + innerPrecedence = PRECEDENCE.get(self.constructor, 0) | |
88 | + args = [extractor.extractWithPrecedence(unhole(arg), innerPrecedence) | |
89 | + for arg in self.arguments] | |
82 | 90 | if self.constructor == "hom": |
83 | - return "[%s, %s]" % (args[0], args[1]) | |
91 | + rv = formatter.formatHom(args[0], args[1]) | |
84 | 92 | elif self.constructor == "pair": |
85 | - return "(%s x %s)" % (args[0], args[1]) | |
93 | + rv = formatter.formatPair(args[0], args[1]) | |
86 | 94 | elif self.constructor == "sum": |
87 | - return "(%s + %s)" % (args[0], args[1]) | |
95 | + rv = formatter.formatSum(args[0], args[1]) | |
88 | 96 | elif self.constructor == "list": |
89 | - return "[%s]" % args[0] | |
97 | + rv = formatter.formatList(args[0]) | |
90 | 98 | else: |
91 | - assert False, "whoops" | |
99 | + assert False, "whoopsie-doodle" | |
100 | + # NB: This is traditionally >, not >= | |
101 | + # but that forgets required strictness and would be confusing to read | |
102 | + if (outerPrecedence >= innerPrecedence and | |
103 | + not formatter.resetsBracketsFor(self.constructor)): | |
104 | + rv = formatter.parenthesize(rv) | |
105 | + return rv | |
92 | 106 | |
93 | 107 | def buildArrow(self): |
94 | 108 | args = [arg.buildArrow() for arg in self.arguments] |
@@ -115,7 +129,7 @@ class Hole(SExp): | ||
115 | 129 | def occurs(self, index): |
116 | 130 | return self.index == index |
117 | 131 | |
118 | - def extractType(self, extractor): | |
132 | + def extractType(self, extractor, formatter, outerPrecedence): | |
119 | 133 | return extractor.findTypeAlias(self.index) |
120 | 134 | |
121 | 135 | def buildArrow(self): |
@@ -80,11 +80,17 @@ class ConstraintStore(object): | ||
80 | 80 | (vi.asStr(), vj.asStr())) |
81 | 81 | |
82 | 82 | |
83 | +class TypeFormatter(object): | |
84 | + """ | |
85 | + Customize the output of a type-extraction operation. | |
86 | + """ | |
87 | + | |
83 | 88 | LETTERS = "XYZWSTPQ" |
84 | 89 | |
85 | 90 | class TypeExtractor(object): |
86 | - def __init__(self, cs): | |
91 | + def __init__(self, cs, formatter): | |
87 | 92 | self.cs = cs |
93 | + self.formatter = formatter | |
88 | 94 | self.d = {} |
89 | 95 | self.seen = [] |
90 | 96 |
@@ -97,13 +103,16 @@ class TypeExtractor(object): | ||
97 | 103 | return self.d[index] |
98 | 104 | |
99 | 105 | def extract(self, var): |
106 | + return self.extractWithPrecedence(var, 0) | |
107 | + | |
108 | + def extractWithPrecedence(self, var, precedence): | |
100 | 109 | if var in self.seen: |
101 | 110 | # XXX split exceptions? |
102 | 111 | raise UnificationFailed("tried to extract infinite type") |
103 | 112 | self.seen.append(var) |
104 | 113 | |
105 | 114 | sexp = self.cs.walk(var) |
106 | - rv = sexp.extractType(self) | |
115 | + rv = sexp.extractType(self, self.formatter, precedence) | |
107 | 116 | |
108 | 117 | self.seen.pop() |
109 | 118 | return rv |
@@ -8,10 +8,29 @@ from cammylib.arrows import BuildProblem, T | ||
8 | 8 | from cammylib.hive import Hive, MissingAtom |
9 | 9 | from cammylib.jelly import jellify |
10 | 10 | from cammylib.parser import parse |
11 | -from cammylib.types import ConstraintStore, TypeExtractor, UnificationFailed | |
11 | +from cammylib.types import ConstraintStore, TypeExtractor, TypeFormatter, UnificationFailed | |
12 | 12 | |
13 | 13 | LINE_BUFFER_LENGTH = 1024 |
14 | 14 | |
15 | +class PlaintextTypeFormatter(TypeFormatter): | |
16 | + def resetsBracketsFor(self, constructor): | |
17 | + return constructor in ("hom", "list") | |
18 | + | |
19 | + def parenthesize(self, x): | |
20 | + return "(%s)" % x | |
21 | + | |
22 | + def formatHom(self, x, y): | |
23 | + return "[%s, %s]" % (x, y) | |
24 | + | |
25 | + def formatPair(self, x, y): | |
26 | + return "%s x %s" % (x, y) | |
27 | + | |
28 | + def formatSum(self, x, y): | |
29 | + return "%s + %s" % (x, y) | |
30 | + | |
31 | + def formatList(self, x): | |
32 | + return "[%s]" % x | |
33 | + | |
15 | 34 | def command(hive, code, line): |
16 | 35 | # XXX code e: Edit a file |
17 | 36 | # os.system("$EDITOR") |
@@ -51,7 +70,7 @@ def repl(hive, stdin, stdout): | ||
51 | 70 | print "Arrow:", arrow |
52 | 71 | cs = ConstraintStore() |
53 | 72 | domain, codomain = arrow.types(cs) |
54 | - extractor = TypeExtractor(cs) | |
73 | + extractor = TypeExtractor(cs, PlaintextTypeFormatter()) | |
55 | 74 | for i, (gdom, gcod) in enumerate(cs.knownGivens): |
56 | 75 | print "Given @%d : %s -> %s" % (i, |
57 | 76 | extractor.extract(gdom), |
@@ -7,11 +7,31 @@ from rpython.rlib.listsort import make_timsort_class | ||
7 | 7 | from cammylib.arrows import BuildProblem |
8 | 8 | from cammylib.hive import Hive, MissingAtom |
9 | 9 | from cammylib.parser import parse |
10 | -from cammylib.types import ConstraintStore, TypeExtractor, UnificationFailed | |
10 | +from cammylib.types import ConstraintStore, TypeExtractor, TypeFormatter, UnificationFailed | |
11 | 11 | |
12 | 12 | |
13 | 13 | SortFileNames = make_timsort_class() |
14 | 14 | |
15 | +class LatexTypeFormatter(TypeFormatter): | |
16 | + def resetsBracketsFor(self, constructor): | |
17 | + return constructor == "list" | |
18 | + | |
19 | + def parenthesize(self, x): | |
20 | + return "( %s )" % x | |
21 | + | |
22 | + def formatHom(self, x, y): | |
23 | + return "{ %s }^{ %s }" % (y, x) | |
24 | + | |
25 | + def formatPair(self, x, y): | |
26 | + return r"%s \times %s" % (x, y) | |
27 | + | |
28 | + def formatSum(self, x, y): | |
29 | + return r"%s + %s" % (x, y) | |
30 | + | |
31 | + def formatList(self, x): | |
32 | + return "[ %s ]" % x | |
33 | + | |
34 | + | |
15 | 35 | def codeblock(code): |
16 | 36 | return "```\n" + code + "\n```" |
17 | 37 |
@@ -45,12 +65,12 @@ def main(argv): | ||
45 | 65 | arrow = sexp.buildArrow() |
46 | 66 | cs = ConstraintStore() |
47 | 67 | domain, codomain = arrow.types(cs) |
48 | - extractor = TypeExtractor(cs) | |
68 | + extractor = TypeExtractor(cs, LatexTypeFormatter()) | |
49 | 69 | for i, (gdom, gcod) in enumerate(cs.knownGivens): |
50 | - doc.append("Given @%d : %s -> %s" % (i, | |
70 | + doc.append(r"Given @%d : $%s \to %s$" % (i, | |
51 | 71 | extractor.extract(gdom), |
52 | 72 | extractor.extract(gcod))) |
53 | - doc.append("Type: %s -> %s" % (extractor.extract(domain), | |
73 | + doc.append(r"Type: $%s \to %s$" % (extractor.extract(domain), | |
54 | 74 | extractor.extract(codomain))) |
55 | 75 | except MissingAtom as ma: |
56 | 76 | doc.append( |
@@ -0,0 +1,8 @@ | ||
1 | +(curry | |
2 | + (comp | |
3 | + (pair | |
4 | + (fun/apppair (comp fst fst) snd) | |
5 | + (fun/apppair (comp fst snd) snd)) | |
6 | + nat/add)) | |
7 | + | |
8 | +Addition of non-standard natural numbers in Baire space. |
@@ -0,0 +1,4 @@ | ||
1 | +id | |
2 | + | |
3 | +As a non-standard natural number, $\omega$ is the smallest natural number | |
4 | +greater than all standard natural numbers. |
@@ -1 +1,3 @@ | ||
1 | -(uncurry nat/eq) | |
1 | +(case nat/is_zero nat/is_zero) | |
2 | + | |
3 | +Whether an integer is zero. |
@@ -1 +1,3 @@ | ||
1 | -fun/swap | |
1 | +sum/swap | |
2 | + | |
3 | +The negation of an integer. |
@@ -1 +1,3 @@ | ||
1 | -(pair (comp fst succ) snd) | |
1 | +(case (comp succ left) (comp nat/pred-maybe (case right (comp nat/1 left)))) | |
2 | + | |
3 | +The successor of an integer. |
@@ -1 +1,3 @@ | ||
1 | -(pair zero zero) | |
1 | +(comp zero left) | |
2 | + | |
3 | +The integer zero. For no particular reason, we choose positive zero. |
@@ -0,0 +1,3 @@ | ||
1 | +(comp zero succ) | |
2 | + | |
3 | +One. |
@@ -1,3 +1,5 @@ | ||
1 | -(pr (curry (comp snd nat/is_zero)) (curry (comp (pair fst (comp snd nat/pred)) fun/app))) | |
1 | +(uncurry (pr | |
2 | + (curry (comp snd nat/is_zero)) | |
3 | + (curry (comp (pair fst (comp snd nat/pred)) fun/app)))) | |
2 | 4 | |
3 | 5 | Equality on natural numbers is decidable. |
@@ -0,0 +1,5 @@ | ||
1 | +(pr | |
2 | + right | |
3 | + (comp (case succ zero) left)) | |
4 | + | |
5 | +The predecessor of a natural number, or a distinguished point for zero. |
@@ -1 +1,3 @@ | ||
1 | 1 | (comp (pr (comp zero fun/dup) (comp fst (pair succ id))) snd) |
2 | + | |
3 | +The predecessor of a natural number. Zero is mapped to itself. |
@@ -0,0 +1 @@ | ||
1 | +(curry snd) |
@@ -0,0 +1,3 @@ | ||
1 | +(case right left) | |
2 | + | |
3 | +Swap the two cases of a sum. |