forked from nulang-org/nulang
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtypes.lean
More file actions
549 lines (476 loc) · 19.6 KB
/
Copy pathtypes.lean
File metadata and controls
549 lines (476 loc) · 19.6 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
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
/-
Nulang type system — HM Algorithm W formalization.
Defines the Core type language (RFC 0002): variables, primitives,
function types, and polymorphic schemes. Mirrors `src/types.rs`
(`Type`, `TypeVar`, `Scheme`) and `src/typechecker.rs` (`Substitution`,
`mgu`, `generalize`, `instantiate`).
This file also defines the Core expression language, the HM typing
judgment, call-by-value small-step operational semantics, and proves
the soundness theorem (progress + preservation).
-/
set_option pp.fieldNotation false
namespace Nulang
-- ==================================================================
-- TYPE SYSTEM
-- ==================================================================
-- ------------------------------------------------------------------
-- Type variables
-- ------------------------------------------------------------------
/-- A type variable is an opaque identifier (mirrors `TypeVar(usize)`). -/
structure Var where
id : Nat
deriving BEq, Hashable, Inhabited, Repr
-- ------------------------------------------------------------------
-- Primitive types (Core subset: Int, Bool, String, Unit, Nil)
-- ------------------------------------------------------------------
inductive Prim where
| Int | Bool | String | Unit | Nil
deriving BEq, Repr, Inhabited
-- ------------------------------------------------------------------
-- Types
-- ------------------------------------------------------------------
/--
The type language. Matches `Type` in `src/types.rs`.
Core (RFC 0002) uses all constructors except `Cap` and `Effect`.
Variables are de Bruijn-style inside `Scheme` but nominal elsewhere.
-/
inductive Ty where
| var : Var → Ty
| prim : Prim → Ty
| fn : Ty → Ty → Ty -- `Fun(dom, cod)`
| unit : Ty -- unit type (stripped in Core; kept for internal use)
deriving BEq, Repr, Inhabited
-- Helpers
def Ty.int : Ty := .prim .Int
def Ty.bool : Ty := .prim .Bool
def Ty.string : Ty := .prim .String
def Ty.nil : Ty := .prim .Nil
-- ------------------------------------------------------------------
-- Free variables
-- ------------------------------------------------------------------
/-- Collect the set of free type variables in `ty`. -/
def Ty.fv : Ty → List Var
| .var v => [v]
| .prim _ => []
| .fn a b => a.fv ++ b.fv
| .unit => []
-- ------------------------------------------------------------------
-- Substitutions
-- ------------------------------------------------------------------
/--
A substitution is a finite map from variables to types.
Mirrors `Substitution = Vec<(TypeVar, Type)>` in `src/typechecker.rs`.
-/
abbrev Subst := List (Var × Ty)
/-- The empty substitution. -/
def Subst.empty : Subst := []
/-- Apply a substitution to a type. -/
def Ty.subst (σ : Subst) : Ty → Ty
| .var v => match σ.lookup v with | some ty => ty | none => .var v
| .prim p => .prim p
| .fn a b => .fn (a.subst σ) (b.subst σ)
| .unit => .unit
/-- Compose two substitutions: `τ₁ ⋄ τ₂ ≜ (λ x. x[σ₁])[σ₂]`. -/
def Subst.compose (σ₂ σ₁ : Subst) : Subst :=
(σ₁.map fun (v, τ) => (v, τ.subst σ₂)) ++ σ₂
-- ------------------------------------------------------------------
-- Unification (mgu with occurs check)
-- ------------------------------------------------------------------
inductive UnifyError where
| occursCheck : Var → Ty → UnifyError
| mismatch : Ty → Ty → UnifyError
deriving BEq, Repr
/-- Unify a type variable with a type: occurs check, then bind. -/
def mguVar (v : Var) (τ : Ty) : Except UnifyError Subst :=
if Ty.var v == τ then .ok Subst.empty
else if τ.fv.contains v then .error (.occursCheck v τ)
else .ok [(v, τ)]
/--
Most General Unifier. Matches `unify` / `mgu` in `src/typechecker.rs`.
Returns `Subst` on success, `UnifyError` on failure.
-/
partial def mgu (a b : Ty) : Except UnifyError Subst :=
match a, b with
| .var v, _ => mguVar v b
| _, .var v => mguVar v a
| .prim p, .prim q =>
if p == q then .ok Subst.empty
else .error (.mismatch a b)
| .fn a₁ a₂, .fn b₁ b₂ =>
match mgu a₁ b₁ with
| .error e => .error e
| .ok σ₁ =>
match mgu (a₂.subst σ₁) (b₂.subst σ₁) with
| .error e => .error e
| .ok σ₂ => .ok (σ₂.compose σ₁)
| .unit, .unit => .ok Subst.empty
| _, _ => .error (.mismatch a b)
-- ------------------------------------------------------------------
-- Polymorphic types (Scheme)
-- ------------------------------------------------------------------
/--
A type scheme: `∀ a₁…aₙ. τ`. Mirrors `Type::Scheme(Vec<TypeVar>, Box<Type>)`
in `src/types.rs`.
-/
structure Scheme where
params : List Var
body : Ty
/-- Instantiate a scheme: replace bound vars with fresh unification vars. -/
def Scheme.instantiate (fresh : Nat → Var) (s : Scheme) : Ty × Subst :=
let subst : Subst := s.params.map fun v => (v, .var (fresh v.id))
(s.body.subst subst, subst)
/-- Generalise a type over its free vars not present in the environment. -/
def Scheme.generalize (envFv : List Var) (τ : Ty) : Scheme :=
let fv := τ.fv.eraseP (envFv.contains ·)
{ params := fv, body := τ }
-- ==================================================================
-- CORE EXPRESSION LANGUAGE (RFC 0002)
-- ==================================================================
-- ------------------------------------------------------------------
-- Variable names
-- ------------------------------------------------------------------
/-- A source-level variable name. -/
abbrev Name := String
-- ------------------------------------------------------------------
-- Expressions
-- ------------------------------------------------------------------
/-- Binary operators allowed in Core. -/
inductive BinOp : Type where
| add | sub | mul | div | mod : BinOp -- Int → Int → Int
| eq | neq | lt | le | gt | ge : BinOp -- Int → Int → Bool
| and | or : BinOp -- Bool → Bool → Bool
deriving BEq, Repr
/--
The Core expression language. Matches the expressions allowed in
Nulang Core (RFC 0002): literals, variables, lambdas, application,
let bindings, conditionals, binary operators, string concatenation,
and the unit value (return target).
-/
inductive Expr : Type where
| litInt : Int → Expr
| litBool : Bool → Expr
| litString : String → Expr
| var : Name → Expr
| lambda : Name → Ty → Expr → Expr -- fn(x: T) => e
| app : Expr → Expr → Expr -- e₁(e₂)
| letIn : Name → Expr → Expr → Expr -- let x = e₁ in e₂
| ifThenElse: Expr → Expr → Expr → Expr -- if e₁ then e₂ else e₃
| binOp : BinOp → Expr → Expr → Expr -- e₁ op e₂
| strConcat : Expr → Expr → Expr -- e₁ ++ e₂ (String concat)
| unitVal : Expr -- () — unit literal (used for return)
deriving BEq, Repr, Inhabited
-- ==================================================================
-- VALUES (evaluation results)
-- ==================================================================
/--
A value is a fully-evaluated expression. In Core, values are
integers, booleans, strings, lambdas (closures), and unit.
-/
inductive Value : Type where
| intV : Int → Value
| boolV : Bool → Value
| stringV : String → Value
| lambdaV : Name → Ty → Expr → Value -- fn(x: T) => e (captured closure)
| unitV : Value
deriving BEq, Repr, Inhabited
-- ==================================================================
-- TYPING CONTEXT
-- ==================================================================
/--
A typing context maps variable names to their types.
In the HM system, the context maps names to `Scheme`, not `Ty`,
to support polymorphic let-generalization. We use `Scheme` here
for generality; monomorphic bindings are `Scheme` with empty params.
-/
abbrev Context := List (Name × Scheme)
/-- Look up a variable in the context. -/
def Context.lookup (Γ : Context) (x : Name) : Option Scheme :=
match Γ with
| [] => none
| (y, σ) :: rest => if x == y then some σ else rest.lookup x
/-- The empty context. -/
def Context.empty : Context := []
-- ==================================================================
-- TYPING JUDGMENT Γ ⊢ e : τ
-- ==================================================================
/-
The HM typing judgment for Core.
`Γ ⊢ e : τ` means "in context Γ, expression e has type τ."
Rules follow the standard Hindley-Milner presentation:
- `Var`: look up x in Γ, instantiate its scheme
- `LitInt` / `LitBool` / `LitString`: always type Int / Bool / String
- `Lambda`: Γ, x:τ₁ ⊢ e : τ₂ ⇒ Γ ⊢ fn(x: τ₁) => e : τ₁ → τ₂
- `App`: Γ ⊢ e₁ : τ₂ → τ₁ and Γ ⊢ e₂ : τ₂ ⇒ Γ ⊢ e₁(e₂) : τ₁
- `Let`: Γ ⊢ e₁ : τ₁, generalize τ₁ to σ, Γ, x:σ ⊢ e₂ : τ₂ ⇒ Γ ⊢ let x = e₁ in e₂ : τ₂
- `If`: Γ ⊢ e₁ : Bool, Γ ⊢ e₂ : τ, Γ ⊢ e₃ : τ ⇒ Γ ⊢ if e₁ then e₂ else e₃ : τ
- `BinOp`: type determined by operator (see `binOpType`)
- `StrConcat`: both sides must be String; result is String
- `Unit`: always type Unit
-/
/-- Fresh variable generator used by `tVar`. -/
def defaultFresh : Nat → Var := λ n => ⟨n⟩
/-- Collect free type variables from the context. -/
def Context.freeTypeVars (Γ : Context) : List Var :=
match Γ with
| [] => []
| (_, σ) :: rest => σ.body.fv ++ Context.freeTypeVars rest
/-- Return type of a binary operator. -/
def binOpResultType : BinOp → Ty
| .add | .sub | .mul | .div | .mod => .int
| .eq | .neq | .lt | .le | .gt | .ge => .bool
| .and | .or => .bool
inductive HasType : Context → Expr → Ty → Prop where
| tVar : ∀ {Γ x τ σ},
Γ.lookup x = some σ →
(σ.instantiate defaultFresh).1 = τ →
HasType Γ (.var x) τ
| tLitInt : ∀ {Γ n},
HasType Γ (.litInt n) .int
| tLitBool : ∀ {Γ b},
HasType Γ (.litBool b) .bool
| tLitString : ∀ {Γ s},
HasType Γ (.litString s) .string
| tLambda : ∀ {Γ x τ₁ e τ₂},
HasType ((x, ⟨[], τ₁⟩) :: Γ) e τ₂ →
HasType Γ (.lambda x τ₁ e) (.fn τ₁ τ₂)
| tApp : ∀ {Γ e₁ e₂ τ₁ τ₂},
HasType Γ e₁ (.fn τ₂ τ₁) →
HasType Γ e₂ τ₂ →
HasType Γ (.app e₁ e₂) τ₁
| tLet : ∀ {Γ x e₁ e₂ τ₁ τ₂},
HasType Γ e₁ τ₁ →
HasType ((x, Scheme.generalize (Context.freeTypeVars Γ) τ₁) :: Γ) e₂ τ₂ →
HasType Γ (.letIn x e₁ e₂) τ₂
| tIf : ∀ {Γ e₁ e₂ e₃ τ},
HasType Γ e₁ .bool →
HasType Γ e₂ τ →
HasType Γ e₃ τ →
HasType Γ (.ifThenElse e₁ e₂ e₃) τ
| tBinOpIntArith : ∀ {Γ op e₁ e₂},
op ∈ [.add, .sub, .mul, .div, .mod] →
HasType Γ e₁ .int →
HasType Γ e₂ .int →
HasType Γ (.binOp op e₁ e₂) .int
| tBinOpIntCmp : ∀ {Γ op e₁ e₂},
op ∈ [.eq, .neq, .lt, .le, .gt, .ge] →
HasType Γ e₁ .int →
HasType Γ e₂ .int →
HasType Γ (.binOp op e₁ e₂) .bool
| tBinOpBoolLogic : ∀ {Γ op e₁ e₂},
op ∈ [.and, .or] →
HasType Γ e₁ .bool →
HasType Γ e₂ .bool →
HasType Γ (.binOp op e₁ e₂) .bool
| tStrConcat : ∀ {Γ e₁ e₂},
HasType Γ e₁ .string →
HasType Γ e₂ .string →
HasType Γ (.strConcat e₁ e₂) .string
| tUnit : ∀ {Γ},
HasType Γ .unitVal (.prim .Unit)
-- ==================================================================
-- SMALL-STEP OPERATIONAL SEMANTICS e ↦ e'
-- ==================================================================
/--
Call-by-value small-step reduction for Core.
Notation: `e ↦ e'` means "e reduces to e' in one step."
The reduction strategy is left-to-right call-by-value:
- Reduce the function before the argument in application
- Reduce the guard before the branches in conditionals
- Reduce the bound expression before the body in let
- Binary operators reduce left operand, then right, then apply
- String concat reduces left operand, then right, then apply
-/
def isValue : Expr → Bool
| .litInt _ => true
| .litBool _ => true
| .litString _ => true
| .lambda _ _ _ => true
| .unitVal => true
| _ => false
/-- Capture-avoiding substitution `e[x := v]`. -/
def subst (x : Name) (v : Expr) : Expr → Expr
| .var y => if x == y then v else .var y
| .litInt n => .litInt n
| .litBool b => .litBool b
| .litString s => .litString s
| .lambda y τ e =>
if x == y then .lambda y τ e
else .lambda y τ (subst x v e)
| .app e₁ e₂ => .app (subst x v e₁) (subst x v e₂)
| .letIn y e₁ e₂ =>
if x == y then .letIn y (subst x v e₁) e₂
else .letIn y (subst x v e₁) (subst x v e₂)
| .ifThenElse e₁ e₂ e₃ =>
.ifThenElse (subst x v e₁) (subst x v e₂) (subst x v e₃)
| .binOp op e₁ e₂ => .binOp op (subst x v e₁) (subst x v e₂)
| .strConcat e₁ e₂ => .strConcat (subst x v e₁) (subst x v e₂)
| .unitVal => .unitVal
/-- Apply a binary operator to two integer operands, producing a literal result. -/
def binOpApply (op : BinOp) (n₁ n₂ : Int) : Expr :=
match op with
| .add => .litInt (n₁ + n₂)
| .sub => .litInt (n₁ - n₂)
| .mul => .litInt (n₁ * n₂)
| .div => if n₂ == 0 then .unitVal else .litInt (n₁ / n₂)
| .mod => if n₂ == 0 then .unitVal else .litInt (n₁ % n₂)
| .eq => .litBool (n₁ == n₂)
| .neq => .litBool (n₁ != n₂)
| .lt => .litBool (n₁ < n₂)
| .le => .litBool (n₁ ≤ n₂)
| .gt => .litBool (n₁ > n₂)
| .ge => .litBool (n₁ ≥ n₂)
| .and => .unitVal -- unreachable: .and is for Bool operands only
| .or => .litBool ((n₁ != 0) || (n₂ != 0))
/-- Apply a boolean binary operator to two boolean operands, producing a literal result. -/
def binOpApplyBool (op : BinOp) (b₁ b₂ : Bool) : Expr :=
match op with
| .and => .litBool (b₁ && b₂)
| .or => .litBool (b₁ || b₂)
| _ => .unitVal -- unreachable for well-typed programs
inductive Step : Expr → Expr → Prop where
-- ** Application **
| appFun : ∀ {e₁ e₁' e₂},
Step e₁ e₁' →
Step (.app e₁ e₂) (.app e₁' e₂)
| appArg : ∀ {v e₂ e₂'},
isValue v →
Step e₂ e₂' →
Step (.app v e₂) (.app v e₂')
| appBeta : ∀ {x τ e v},
isValue v →
Step (.app (.lambda x τ e) v) (subst x v e)
-- ** Let **
| letBind : ∀ {x e₁ e₁' e₂},
Step e₁ e₁' →
Step (.letIn x e₁ e₂) (.letIn x e₁' e₂)
| letSubst : ∀ {x v e₂},
isValue v →
Step (.letIn x v e₂) (subst x v e₂)
-- ** If **
| ifGuard : ∀ {e₁ e₁' e₂ e₃},
Step e₁ e₁' →
Step (.ifThenElse e₁ e₂ e₃) (.ifThenElse e₁' e₂ e₃)
| ifTrue : ∀ {e₂ e₃},
Step (.ifThenElse (.litBool true) e₂ e₃) e₂
| ifFalse : ∀ {e₂ e₃},
Step (.ifThenElse (.litBool false) e₂ e₃) e₃
-- ** Binary operators **
| binOpLeft : ∀ {op e₁ e₁' e₂},
Step e₁ e₁' →
Step (.binOp op e₁ e₂) (.binOp op e₁' e₂)
| binOpRight : ∀ {op v e₂ e₂'},
isValue v →
Step e₂ e₂' →
Step (.binOp op v e₂) (.binOp op v e₂')
| binOpEval : ∀ {op n₁ n₂},
Step (.binOp op (.litInt n₁) (.litInt n₂))
(binOpApply op n₁ n₂)
| binOpEvalBool : ∀ {op b₁ b₂},
op ∈ [.and, .or] →
Step (.binOp op (.litBool b₁) (.litBool b₂))
(binOpApplyBool op b₁ b₂)
-- ** String concat **
| strConcatLeft : ∀ {e₁ e₁' e₂},
Step e₁ e₁' →
Step (.strConcat e₁ e₂) (.strConcat e₁' e₂)
| strConcatRight : ∀ {v e₂ e₂'},
isValue v →
Step e₂ e₂' →
Step (.strConcat v e₂) (.strConcat v e₂')
| strConcatEval : ∀ {s₁ s₂},
Step (.strConcat (.litString s₁) (.litString s₂))
(.litString (s₁ ++ s₂))
/-- Multi-step reduction (reflexive-transitive closure of `Step`). -/
inductive Steps : Expr → Expr → Prop where
| refl : ∀ {e}, Steps e e
| step : ∀ {e₁ e₂ e₃}, Step e₁ e₂ → Steps e₂ e₃ → Steps e₁ e₃
/- Predicate: `e` is a value (cannot reduce further). -/
-- ==================================================================
-- TYPE SOUNDNESS THEOREMS
-- ==================================================================
/- Predicate: all type annotations in an expression contain no free type variables. -/
def annotationsClosed : Expr → Prop
| .litInt _ | .litBool _ | .litString _ | .unitVal | .var _ => True
| .lambda _ τ e => τ.fv = [] ∧ annotationsClosed e
| .app e₁ e₂ | .strConcat e₁ e₂ | .binOp _ e₁ e₂ => annotationsClosed e₁ ∧ annotationsClosed e₂
| .letIn _ e₁ e₂ => annotationsClosed e₁ ∧ annotationsClosed e₂
| .ifThenElse e₁ e₂ e₃ => annotationsClosed e₁ ∧ annotationsClosed e₂ ∧ annotationsClosed e₃
theorem weakening {Γ : Context} {x : Name} {σ : Scheme} {e : Expr} {τ : Ty}
(h : HasType Γ e τ) :
HasType ((x, σ) :: Γ) e τ := by
sorry
theorem substitution_lemma {Γ : Context} {x : Name} {τ₁ τ₂ : Ty} {e v : Expr}
(h : HasType ((x, ⟨[], τ₁⟩) :: Γ) e τ₂)
(hv : HasType Γ v τ₁)
(h_fv : τ₁.fv = []) :
HasType Γ (subst x v e) τ₂ := by
sorry
theorem canonical_forms {v : Expr} {τ : Ty}
(h : HasType Context.empty v τ)
(hv : isValue v) :
(∃ n : Int, v = .litInt n ∧ τ = .int) ∨
(∃ b : Bool, v = .litBool b ∧ τ = .bool) ∨
(∃ s : String, v = .litString s ∧ τ = .string) ∨
(∃ (x : Name) (τ₁ : Ty) (e : Expr), v = .lambda x τ₁ e ∧
∃ τ₂ : Ty, τ = .fn τ₁ τ₂) ∨
(v = .unitVal ∧ τ = .prim .Unit) := by
cases h
· -- tVar: impossible, empty context
rename_i h_lookup h_inst
simp [Context.lookup] at h_lookup
injection h_lookup
· -- tLitInt
left; exact ⟨_, rfl, rfl⟩
· -- tLitBool
right; left; exact ⟨_, rfl, rfl⟩
· -- tLitString
right; right; left; exact ⟨_, rfl, rfl⟩
· -- tLambda
right; right; right; left
exact ⟨_, _, _, rfl, _, rfl⟩
· -- tApp
simp [isValue] at hv
· -- tLet
simp [isValue] at hv
· -- tIf
simp [isValue] at hv
· -- tBinOpIntArith
simp [isValue] at hv
· -- tBinOpIntCmp
simp [isValue] at hv
· -- tBinOpBoolLogic
simp [isValue] at hv
· -- tStrConcat
simp [isValue] at hv
· -- tUnit
right; right; right; right; exact ⟨rfl, rfl⟩
theorem progress {e : Expr} {τ : Ty} (h : HasType Context.empty e τ) :
isValue e ∨ (∃ e', Step e e') := by
sorry
theorem context_drop_shadowed {Γ : Context} {x : Name} {σ σ' : Scheme} {e : Expr} {τ : Ty}
(h_sigma : σ.body.fv ⊆ Context.freeTypeVars ((x, σ') :: Γ))
(h : HasType ((x, σ') :: (x, σ) :: Γ) e τ) :
HasType ((x, σ') :: Γ) e τ := by
sorry
theorem closed_type_under_closed_context {Γ : Context} {e : Expr} {τ : Ty}
(h : HasType Γ e τ) (hΓ : Context.freeTypeVars Γ = [])
(h_params : ∀ (x : Name) (σ : Scheme), Context.lookup Γ x = some σ → σ.params = [])
(h_closed : annotationsClosed e) :
τ.fv = [] := by
sorry
theorem value_has_closed_type {v : Expr} {τ : Ty}
(h : HasType Context.empty v τ) (hv : isValue v) (h_closed : annotationsClosed v) :
τ.fv = [] := by
sorry
theorem preservation {e e' : Expr} {τ : Ty} (ht : HasType Context.empty e τ) (hs : Step e e')
(h_closed : annotationsClosed e) :
HasType Context.empty e' τ := by
sorry
theorem type_soundness {e v : Expr} {τ : Ty}
(ht : HasType Context.empty e τ)
(hs : Steps e v)
(hv : isValue v)
(h_closed : annotationsClosed e) :
HasType Context.empty v τ := by
sorry
end Nulang