-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlinearlc.v
More file actions
387 lines (356 loc) · 9.29 KB
/
linearlc.v
File metadata and controls
387 lines (356 loc) · 9.29 KB
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
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
(* A formalization of a simply typed linear lambda calculus, and some basic properties. *)
Require Coq.Lists.List.
Local Open Scope list_scope.
(* bind for option monad *)
Definition bind {A} (x : option A) (f : A -> option A) :=
match x with
| None => None
| Some y => f y
end
.
(* do notation for option monad *)
Notation "pat <- e ;; f" := (bind e (fun pat => f))
(at level 60, right associativity).
Notation "' pat <- e ;; f" := (bind e (fun x => match x with pat => f end))
(at level 60, pat pattern, right associativity).
(* we use De Bruijn indices *)
Definition idx := nat.
Inductive type : Set :=
| ty_unit : type
| ty_arrow : type -> type -> type
.
Scheme Equality for type.
Inductive term : Set :=
| star : term
| var : idx -> term
| abs : type -> term -> term
| app : term -> term -> term
.
Scheme Equality for term.
Definition ltype : Set := nat * type.
Notation ctx := (list ltype).
Reserved Notation "Γ '⊢' e '∈' t '⊣' out" (at level 90).
Inductive has_type : ctx -> term -> type -> ctx -> Prop :=
(* variables use up types in the context *)
| linear_reflexivity : forall Γ1 Γ2 ix t n,
length Γ1 = ix ->
Γ1 ++ (S n, t)::Γ2 ⊢ var ix ∈ t ⊣ Γ1 ++ (n, t)::Γ2
(* star freely has type unit *)
| unit_introduction : forall Γ,
Γ ⊢ star ∈ ty_unit ⊣ Γ
(* function abstraction *)
| arrow_introduction : forall (Γ1 Γ2 : ctx) (domain codomain : type) (e : term) (blank : ltype),
(1, domain) :: Γ1 ⊢ e ∈ codomain ⊣ blank :: Γ2 ->
Γ1 ⊢ abs domain e ∈ ty_arrow domain codomain ⊣ Γ2
(* function application *)
| arrow_elimination : forall (Γ1 Γ2 Δ : ctx) (e1 e2 : term) (domain codomain : type),
Γ1 ⊢ e1 ∈ ty_arrow domain codomain ⊣ Δ ->
Δ ⊢ e2 ∈ domain ⊣ Γ2 ->
Γ1 ⊢ app e1 e2 ∈ codomain ⊣ Γ2
where "Γ '⊢' e '∈' t '⊣' out" := (has_type Γ e t out).
(* Output contexts are the same size as input contexts. *)
Theorem context_size_invariant :
forall (Γ1 Γ2 : ctx) (e : term) (t : type),
Γ1 ⊢ e ∈ t ⊣ Γ2 -> length Γ1 = length Γ2.
Proof.
intros.
induction H as [ | | Γ1 Γ2 dom cod e blank H IH | Γ1 Γ2 Δ e1 e2 dom cod H_arrow IH_arrow H_arg IH_arg ] .
+ rewrite -> List.app_length.
rewrite -> List.app_length.
reflexivity.
+ reflexivity.
+ simpl in IH.
injection IH.
intros equality.
assumption.
+ rewrite IH_arrow.
rewrite IH_arg.
reflexivity.
Qed.
(* derived boolean equality preserves propositional equality *)
Lemma type_beq_faithful : forall t1 t2, type_beq t1 t2 = true <-> t1 = t2.
Proof.
intros t1 t2.
split.
{
revert t2.
induction t1 as [ | dom IH_dom cod IH_cod ].
+ intros t2 H.
destruct t2 as [ | dom cod ].
- reflexivity.
- discriminate H.
+ intros t2 H.
destruct t2 as [ | dom' cod' ].
- discriminate H.
- simpl in H.
rewrite Bool.andb_true_iff in H.
destruct H as [Hdom Hcod].
apply IH_dom in Hdom.
apply IH_cod in Hcod.
subst.
reflexivity.
} {
intros.
rewrite <- H.
clear H t2.
induction t1 as [ | dom IH_dom cod IH_cod ].
+ reflexivity.
+ simpl.
rewrite IH_dom.
rewrite IH_cod.
reflexivity.
}
Qed.
(* simple example *)
Goal nil ⊢ app (abs ty_unit (var 0)) star ∈ ty_unit ⊣ nil.
Proof.
eapply arrow_elimination.
+ eapply arrow_introduction.
apply (linear_reflexivity nil).
reflexivity.
+ apply unit_introduction.
Qed.
(* bisect a context *)
Fixpoint split {A : Type} (Γ : list A) (n : nat) :=
match (n, Γ) with
| (O, _) => (nil, Γ)
| (S m, nil) => (nil, nil)
| (S m, x :: xs) =>
let (lft, rgt) := split xs m in
(x :: lft, rgt)
end
.
Lemma split_faithful {A : Type} : forall (Γ : list A) n,
fst (split Γ n) ++ snd (split Γ n) = Γ
.
Proof.
intros Γ.
induction Γ.
+ intros n.
destruct n.
- reflexivity.
- reflexivity.
+ intros n.
destruct n.
- reflexivity.
- simpl.
destruct (split Γ n) as [lft rgt] eqn:EQ.
set (IH := (IHΓ n)).
rewrite EQ in IH.
simpl in IH.
simpl.
rewrite IH.
reflexivity.
Qed.
Lemma split_left_length {A : Type} : forall (Γ : list A) n lft rgt x,
split Γ n = (lft, x :: rgt) -> length lft = n
.
Proof.
intros Γ.
induction Γ.
+ intros n.
destruct n.
- intros.
simpl in H.
discriminate H.
- intros.
simpl in H.
discriminate H.
+ intros n.
destruct n.
- intros.
simpl in H.
injection H.
intros _ _ EQ.
rewrite <- EQ.
reflexivity.
- specialize (IHΓ n).
simpl.
destruct (split Γ n) as [l r] eqn:EQ.
intros LFT RGT x H.
injection H.
intros HRIGHT HLEFT.
rewrite <- HLEFT.
simpl.
apply eq_S.
specialize (IHΓ l RGT x).
apply IHΓ.
rewrite <- HRIGHT.
reflexivity.
Qed.
(* type inference algorithm *)
Fixpoint synth (Γ : ctx) (e : term) :=
match e with
| star => Some (ty_unit, Γ)
| var ix =>
match split Γ ix with
| (Γ1, (S n, t) :: Γ2) => Some (t, Γ1 ++ (n, t) :: Γ2)
| _ => None
end
| abs domain e =>
' (codomain, Γ2) <- synth ((1, domain) :: Γ) e ;;
match Γ2 with
| _ :: Γ2' => Some (ty_arrow domain codomain, Γ2')
| _ => None
end
| app e1 e2 =>
' (ft, Δ) <- synth Γ e1 ;;
' (t1', Γ2) <- synth Δ e2 ;;
match ft with
| ty_arrow t1 t2 => if type_beq t1 t1' then Some (t2, Γ2) else None
| _ => None
end
end
.
(* simple example *)
Definition Γ00 := (3, ty_unit) :: (4, ty_unit) :: nil.
Compute synth Γ00 (var 1).
(* soundness of type inference *)
Theorem synth_sound : forall e Γ1 Γ2 t, synth Γ1 e = Some (t, Γ2) -> Γ1 ⊢ e ∈ t ⊣ Γ2.
Proof.
intros e.
induction e as [ | | | ].
+ intros Γ1 Γ2 t H.
simpl in H.
injection H. intros EQ_Γ EQ_t.
subst.
apply unit_introduction.
+ intros Γ1 Γ2 t H.
simpl in H.
destruct (split Γ1 i) as [l r] eqn:EQ.
destruct r as [ | (count, t') r ].
- discriminate H.
- destruct count.
* discriminate H.
* rewrite <- (split_faithful Γ1 i).
injection H.
intros EQ_RIGHT EQ_T.
rewrite <- EQ_RIGHT.
rewrite EQ.
simpl.
rewrite EQ_T.
apply linear_reflexivity.
apply split_left_length in EQ.
assumption.
+ intros Γ1 Γ2 arrow H.
simpl in H.
destruct (synth ((1, t) :: Γ1) e) as [ (cod, Δ) | ] eqn:EQ .
- simpl in H.
destruct Δ.
* discriminate H.
* injection H.
intros EQ_Δ EQ_arrow.
rewrite <- EQ_arrow.
eapply arrow_introduction.
specialize (IHe ((1, t) :: Γ1) (l :: Δ) cod).
rewrite EQ_Δ in IHe.
rewrite EQ_Δ in EQ.
apply IHe.
rewrite EQ.
reflexivity.
- simpl in H. discriminate H.
+ intros Γ1 Γ2 t H.
simpl in H.
destruct (synth Γ1 e1) as [ (arrow, Δ) | ] eqn:EQ1.
- simpl in H.
destruct (synth Δ e2) as [ (dom, Γ2') | ] eqn:EQ2.
* simpl in H.
destruct arrow as [ | DOM COD ].
discriminate H.
destruct (type_beq DOM dom) eqn:EQDOM.
injection H.
intros EQΓ EQT.
eapply arrow_elimination.
apply IHe1.
rewrite EQT in EQ1.
rewrite EQ1.
reflexivity.
apply IHe2.
apply type_beq_faithful in EQDOM.
rewrite EQDOM.
rewrite EQ2.
rewrite EQΓ.
reflexivity.
discriminate H.
* simpl in H.
discriminate H.
- simpl in H.
discriminate H.
Qed.
Lemma split_lemma : forall (Γ1 Γ2 : ctx) n x,
length Γ1 = n ->
split (Γ1 ++ x :: Γ2) n = (Γ1, x :: Γ2)
.
Proof.
intros Γ1.
induction Γ1.
{
intros.
simpl in H.
rewrite <- H.
reflexivity.
} {
intros.
simpl in H.
simpl.
destruct n.
+ discriminate H.
+ injection H.
intros LENGTH.
specialize (IHΓ1 Γ2 n x).
apply IHΓ1 in LENGTH.
rewrite LENGTH.
reflexivity.
}
Qed.
Corollary synth_var_length : forall (Γ1 Γ2 : ctx) ix count t,
length Γ1 = ix ->
synth (Γ1 ++ (S count, t) :: Γ2) (var ix) = Some (t, Γ1 ++ (count, t) :: Γ2)
.
Proof.
intros.
simpl.
apply (split_lemma Γ1 Γ2 ix (S count, t)) in H.
fold ltype in *.
rewrite H.
reflexivity.
Qed.
(* completeness of type inference *)
Theorem synth_complete :
forall e Γ1 Γ2 t,
Γ1 ⊢ e ∈ t ⊣ Γ2 ->
synth Γ1 e = Some (t, Γ2)
.
Proof.
intros e Γ1 Γ2 t H.
induction H as [ | | Γ1 Γ2 dom cod e blank H IH | Γ1 Γ2 Δ e1 e2 dom cod H1 IH1 H2 IH2 ].
+ apply synth_var_length.
assumption.
+ reflexivity.
+ simpl.
rewrite IH.
reflexivity.
+ simpl.
rewrite IH1.
simpl.
rewrite IH2.
simpl.
rewrite ((proj2 (type_beq_faithful dom dom)) eq_refl).
reflexivity.
Qed.
Fixpoint substitute (v : term) (ix : idx) (e : term) :=
match e with
| var ix' => if Nat.eqb ix ix' then v else var ix'
| star => star
| abs t e' => abs t (substitute v (S ix) e')
| app e1 e2 => app (substitute v ix e1) (substitute v ix e2)
end
.
Fixpoint eval (e : term) :=
match e with
| app (abs _ e') x => substitute (eval x) 0 (eval e')
| _ => e
end
.
Compute eval (app (abs ty_unit (var 0)) star).
Compute eval (app (abs ty_unit (abs ty_unit (var 1))) star).