-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPureScriptAST.lean
More file actions
167 lines (131 loc) · 5.39 KB
/
Copy pathPureScriptAST.lean
File metadata and controls
167 lines (131 loc) · 5.39 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
/-
PureScript AST in Lean 4
A minimal but complete AST for representing PureScript expressions,
with pretty-printing and rfl-provable structural properties.
-/
-- ===== Core AST =====
/-- Simple PureScript expression AST -/
inductive PSExpr where
| Var : String → PSExpr
| Lam : String → PSExpr → PSExpr
| App : PSExpr → PSExpr → PSExpr
| Let : String → PSExpr → PSExpr → PSExpr
| Lit : String → PSExpr -- String/Int/Bool literals as strings
deriving Repr, BEq, Inhabited
namespace PSExpr
/-- Pretty print a PSExpr to PureScript syntax -/
partial def pretty : PSExpr → String
| .Var n => n
| .Lam arg body => s!"\\{arg} -> {body.pretty}"
| .App f arg =>
let argStr := match arg with
| .App _ _ => s!"({arg.pretty})"
| _ => arg.pretty
s!"{f.pretty} {argStr}"
| .Let name val body => s!"let {name} = {val.pretty} in {body.pretty}"
| .Lit s => s
/-- Check if an expression is a simple value (no computation) -/
def isValue : PSExpr → Bool
| .Var _ => true
| .Lam _ _ => true
| .Lit _ => true
| _ => false
/-- Count the number of lambda abstractions -/
def countLambdas : PSExpr → Nat
| .Lam _ body => 1 + body.countLambdas
| .App f arg => f.countLambdas + arg.countLambdas
| .Let _ val body => val.countLambdas + body.countLambdas
| _ => 0
/-- Collect all free variables -/
partial def freeVars (bound : List String := []) : PSExpr → List String
| .Var n => if bound.contains n then [] else [n]
| .Lam arg body => body.freeVars (arg :: bound)
| .App f arg => f.freeVars bound ++ arg.freeVars bound
| .Let name val body => val.freeVars bound ++ body.freeVars (name :: bound)
| .Lit _ => []
end PSExpr
-- ===== Standard Combinators =====
/-- Identity: λx. x -/
def I : PSExpr := .Lam "x" (.Var "x")
/-- Constant: λx. λy. x -/
def K : PSExpr := .Lam "x" (.Lam "y" (.Var "x"))
/-- Substitution: λf. λg. λx. f x (g x) -/
def S : PSExpr :=
.Lam "f" (.Lam "g" (.Lam "x"
(.App (.App (.Var "f") (.Var "x")) (.App (.Var "g") (.Var "x")))))
/-- Composition: λf. λg. λx. f (g x) -/
def B : PSExpr :=
.Lam "f" (.Lam "g" (.Lam "x"
(.App (.Var "f") (.App (.Var "g") (.Var "x")))))
/-- Flip: λf. λx. λy. f y x -/
def C : PSExpr :=
.Lam "f" (.Lam "x" (.Lam "y"
(.App (.App (.Var "f") (.Var "y")) (.Var "x"))))
/-- Self-application: λx. x x -/
def omega : PSExpr := .Lam "x" (.App (.Var "x") (.Var "x"))
/-- Y combinator: λf. (λx. f (x x)) (λx. f (x x)) -/
def Y : PSExpr :=
let inner := PSExpr.Lam "x" (.App (.Var "f") (.App (.Var "x") (.Var "x")))
.Lam "f" (.App inner inner)
-- ===== Church Encodings =====
def churchTrue : PSExpr := .Lam "t" (.Lam "f" (.Var "t"))
def churchFalse : PSExpr := .Lam "t" (.Lam "f" (.Var "f"))
def churchAnd : PSExpr :=
.Lam "p" (.Lam "q" (.App (.App (.Var "p") (.Var "q")) churchFalse))
def churchOr : PSExpr :=
.Lam "p" (.Lam "q" (.App (.App (.Var "p") churchTrue) (.Var "q")))
def churchNot : PSExpr :=
.Lam "p" (.App (.App (.Var "p") churchFalse) churchTrue)
-- ===== Structural Proofs (rfl) =====
/-- Identity is structurally a single-arg lambda returning its arg -/
theorem I_structure : I = PSExpr.Lam "x" (.Var "x") := rfl
/-- K is structurally a two-arg lambda returning the first -/
theorem K_structure : K = PSExpr.Lam "x" (.Lam "y" (.Var "x")) := rfl
/-- B (composition) is equal to our compose definition -/
theorem B_is_compose : B = PSExpr.Lam "f" (.Lam "g" (.Lam "x"
(.App (.Var "f") (.App (.Var "g") (.Var "x"))))) := rfl
/-- I is a value -/
theorem I_is_value : I.isValue = true := rfl
/-- omega is a value -/
theorem omega_is_value : omega.isValue = true := rfl
/-- I has exactly 1 lambda -/
theorem I_has_one_lambda : I.countLambdas = 1 := rfl
/-- K has exactly 2 lambdas -/
theorem K_has_two_lambdas : K.countLambdas = 2 := rfl
/-- S has exactly 3 lambdas -/
theorem S_has_three_lambdas : S.countLambdas = 3 := rfl
/-- Church true has similar structure to K (both return first of two args) -/
theorem church_true_structure : churchTrue = PSExpr.Lam "t" (.Lam "f" (.Var "t")) := rfl
-- ===== Demo =====
def main : IO Unit := do
IO.println "=== PureScript AST in Lean 4 ===\n"
IO.println "-- Combinators --"
IO.println s!"I = {I.pretty}"
IO.println s!"K = {K.pretty}"
IO.println s!"S = {S.pretty}"
IO.println s!"B = {B.pretty}"
IO.println s!"C = {C.pretty}"
IO.println s!"Y = {Y.pretty}"
IO.println s!"ω = {omega.pretty}"
IO.println "\n-- Church Booleans --"
IO.println s!"true = {churchTrue.pretty}"
IO.println s!"false = {churchFalse.pretty}"
IO.println s!"and = {churchAnd.pretty}"
IO.println s!"or = {churchOr.pretty}"
IO.println s!"not = {churchNot.pretty}"
IO.println "\n-- Properties (proven with rfl) --"
IO.println s!"I.isValue = {I.isValue}"
IO.println s!"I.countLambdas = {I.countLambdas}"
IO.println s!"K.countLambdas = {K.countLambdas}"
IO.println s!"S.countLambdas = {S.countLambdas}"
IO.println s!"churchTrue structure matches K pattern"
IO.println "\n-- Theorems --"
IO.println "✓ I_structure : I = Lam \"x\" (Var \"x\")"
IO.println "✓ K_structure : K = Lam \"x\" (Lam \"y\" (Var \"x\"))"
IO.println "✓ B_is_compose"
IO.println "✓ I_is_value : I.isValue = true"
IO.println "✓ I_has_one_lambda : I.countLambdas = 1"
IO.println "✓ K_has_two_lambdas : K.countLambdas = 2"
IO.println "✓ S_has_three_lambdas : S.countLambdas = 3"
IO.println "✓ church_true_structure"
#eval main