-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathREADME
324 lines (212 loc) · 8.88 KB
/
README
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
Chariot
=======
A language with arbitrary nested inductive and coinductive types
================================================================
Installation
------------
=== Prerequisites ===
a standard OCaml distribution with ocamlbuild
=== Build ===
$ make
=== Usage ===
$ ./chariot
or
$ ./chariot -i file1.ch file2.ch ...
to have readline capabilities, use rlwrap or ledit
$ rlwrap -H ~/.chariot_history ./chariot --colors
or
$ ledit -h ~/.chariot_history -x -u -l $COLUMNS ./chariot --colors
(note the -u switch for unicode and the -l switch to prevent line cutting)
Toplevel and Commands
---------------------
Each command to the toplevel needs to end with a ";" or a blank line.
The top level interprets type definitions (:help types) and recursive
definitions (:help def) and adds them to the context.
A single term given after the prompt is evaluated and the result is printed.
Structures are evaluated lazily and the system prints a "hole" with a number
for each subterm whose evaluation is frozen:
# arith 0 1;
>> term: arith 0 1
>> of type: stream(nat)
>> result: {
>> Head = <1> ;
>> Tail = <2> }
<1> and <2> are examples of such holes.
To reduce them, we give the command
# > 1,2
If no hole is given, the system with evaluate all the holes...
The other commands are
:help to display this help
:help types to display help about type definitions
:help def to display help about value definitions
:help commands to display this message
:help syntax to display help about chariot's syntax
:set to list the available options with there current value
:set <option> <value> to change the value of an optiond
:show <name> to show a function definition or a type definition
:show env to show the whole environment
:show types to show all the type definitions
:show functions to show all the recursive definitions
:type <term> to infer the type of a term and show the types of all its components
:reduce <term> to reduce a term and show the result
:unfold <term>, <depth> to reduce a term and unfold all its frozen components, up to a given depth
:echo <string> to echo some statis message
:quit to quit!
Comments
--------
Comments are either
- single line comments started with "--" (anywhere on a line)
- multiline comments delimited with "(*" and "*)".
Syntax
------
//Types// (:help types) are given by the syntax
t ::= 'x | name(t1,...,tn) | t1 -> t2
Variables 'x are universally quantified (ML polymorphism) and a type name
without parameters can be written without parenthesis.
As usual, the "->" constructor is right associative: "t1 -> t2 -> t3" really
means "t1 -> (t2 -> t3)".
//Terms// are given by the syntax
u ::= x | C | u.D | { D1 = u1 ; ... ; Dn = un } | u1 u2
where
- "x" is a variables
- "C" is a constructor
- "D", "D1", ..., "Dn" are destructors
- application is left associative: "u1 u2 u3" really means "(u1 u2) u3".
There are two additional term constructors:
- "???" for a "generic metavariable" (possibly shown as "✠" by the system)
- "!!!" for a "generic looping term" (possibly show as "Ω" by the system)
Those terms can take any type and differ only in how they behave during
evaluation / totality checking.
In particular, the system always assumes that "!!!" loops and that "???" is as
good as possible. (The system tries to check that it could be filled with an
appropriate term making the definition total.)
//Constructor patterns// are a subset of terms. They are given by
p ::= _ | x | C | { D1 = u1 ; ... ; Dn = un }
The special variable "_" corresponds to a variable that will not be used in
the right-side of the corresponding clause.
//Clause patterns// are used to define recursive values. They are given by the
syntax
cp ::= f | cp p | cp.D
where
- "f" is a variable name (corresponding to one of the values being defined)
- "p" is a constructor pattern
- "D" is a destructor.
Syntactic sugar (types)
-----------------------
There is a single syntactic sugar for types: "t1 * t2 * t3" really means
"prod_3(t1,t2,t3)". Of course, the type prod_3 needs to be defined in order to
use this...
Syntactic sugar (terms)
-----------------------
Several syntactic sugar are available at the level of terms:
- a decimal integer is expanded to Succ (Succ ... Zero)
- "v + k" where "k" is a decimal integer is expanded to Succ (Succ ... v)
For example, the following is a valid definition
val fib 0 = 0
| fib 1 = 1
| fib (n+2) = add (fib (n+1)) (fib n)
- Lists can be given using Caml notation:
- "[]" for the empty list "Nil"
- "x::xs" for "Cons x xs"
- "[x1; x2; ...; xn]" for "Cons x1 (Cons x2 ... (Cons xn Nil)...)"
For example, the following is a valid definition
val rev_append [] l = l
| rev_append (x::xs) l = rev_append xs (x::l)
- Tuple notation corresponds to special constructors Tuple_0, Tuple_1, ...
For example, "(a,b,c)" really means "Tuple_3 a b c".
- $ can be used for right-associative application: "u1 $ u2 $ u3 u4" really
means "u1 (u2 (u3 u4))"
- a structure with a single label (destructor) D can be written as
with a #: "D#v" really means "{ D = v }".
- it is possible to use "fun x1 x2 -> v" inside a definition. This is
translated into a additional (mutually recurive) function with clause
_fun_aux ... x1 x2 = v
where "..." contains all the free variables from the original clause.
The system tries to display natural numbers (constructors Succ and Zero),
lists (constructors Nil and Cons) and tuples (contructors Tuple_0, Tuple_1,
...) using the corresponding syntactic sugar. Each can be turned off with the
options "show_nats", "show_lists" and "show_tuples".
Recursive Definitions
---------------------
A definition is of the form
val f : t1 -> t2 -> ... -> tn
| clause0 = v0
| clause1 = v1
| ...
where
- each "clause" is a clause pattern (:help syntax)
- each "v" is a term (:help syntax)
The type is only strictly necessary for function without any defining clause
(their codomain is empty). In all other cases, the type will be infered if not given.
For example:
val add n Zero = n
| add n (Succ m) = Succ (add n m)
val bot : empty -> 'a -- empty is a type with no constructor
-- no defining clause
val map : ('a -> 'b) -> list('a) -> list('b)
| map _ Nil = Nil
| map f (Cons x xs) = Cons (f x) (map f xs)
val (map_stream f s).Head = f (s.Head)
| (map_stream f s).Tail = map_stream f (s.Tail)
val branch : tree('b,'x) -> list('b) -> list('x)
| branch t Nil = t.Root
| branch t (Cons b bs) = Cons (t.Root) (branch (t.Branch b) bs)
Types
-----
Types come in two flavors: strict/inductive or lazy/coinductive.
A strict/inductive type is defined with
data tname(params)
where C1 : t0 -> t1 -> ... -> tn -> tname(params)
| C2 : ...
| ...
Each Ci is a *constructor* and its type must end with tname(params).
(Each of the tj can also be tname(params) in order to define an
inductive type.)
For example
data nat
where Zero : nat
| Succ : nat -> nat
data list('a)
where Nil : list('a)
| Cons : 'a -> list('a) -> list('a)
data empty
where
-- no constructor!
A lazy/coinductive type is defined with
codata tname(params)
where D1 : tname(params) -> t1 -> t2 -> ... -> tn
| D2 : ...
| ...
Each Di is a *destructor* and its type must start with tname(params).
(Each of the tj can also be tname(params) in order to define a
coinductive type.)
There cannot be "nullary" destructors D : tname(params).
For example
codata stream('x)
where Head : stream('x) -> 'x
| Tail : stream('x) -> stream('x)
codata prod('a,'b)
where Fst : prod('a,'b) -> 'a
| Snd : prod('a,'b) -> 'b
codata one
where
-- no destructor!
codata tree('b,'x) -- infinite trees branching on 'b with data in 'x
where Root : tree('b,'x) -> 'x
| Branch : tree('b,'x) -> 'b -> tree('b,'x)
Note: there can be at most one type with zero destructors (like "one" above).
Note: to define a coinductive type with several "constructors", we have
to define an auxiliary strict parametrized type:
data colist_aux('x,'l)
where Nil : colist_aux('x,'l)
| Cons : 'x -> 'l -> colist_aux('x,'l)
codata colist('x)
where D : colist('x) -> colist_aux('x,colist('x))
or
data process_aux('p)
where End : process_aux('p)
| Out : nat -> process_aux('p)
| In : nat -> (nat -> 'p) -> process_aux('p)
codata process
where D : process -> process_aux(process)
Note: no two constructors / destructor may have the same name.