tablambda 0.6.0.post30.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tablambda/__init__.py +1 -0
- tablambda/_analysis.py +92 -0
- tablambda/_ast.py +289 -0
- tablambda/_binnat.py +170 -0
- tablambda/_codec.py +147 -0
- tablambda/_compiler_artifact.py +50 -0
- tablambda/_defun_codegen.py +324 -0
- tablambda/_defun_runtime.py +207 -0
- tablambda/_defunctionalize.py +455 -0
- tablambda/_dsl.py +148 -0
- tablambda/_generated/.gitattributes +5 -0
- tablambda/_generated/__init__.py +1 -0
- tablambda/_generated/_generated_defun_compiler_py311.py +7579 -0
- tablambda/_generated/_generated_defun_compiler_py312.py +7579 -0
- tablambda/_generated/_generated_defun_compiler_py313.py +7579 -0
- tablambda/_hoas_latex.py +144 -0
- tablambda/_latex.py +56 -0
- tablambda/_prelude.py +163 -0
- tablambda/_pyast.py +416 -0
- tablambda/_pybuild.py +315 -0
- tablambda/_reduce.py +145 -0
- tablambda/_shape.py +129 -0
- tablambda/_sugar.py +74 -0
- tablambda/_typecheck.py +370 -0
- tablambda-0.6.0.post30.dev0.dist-info/METADATA +45 -0
- tablambda-0.6.0.post30.dev0.dist-info/RECORD +28 -0
- tablambda-0.6.0.post30.dev0.dist-info/WHEEL +4 -0
- tablambda-0.6.0.post30.dev0.dist-info/entry_points.txt +2 -0
tablambda/_typecheck.py
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
"""Simple-typability as a lambda term: algorithm-W (STLC) written in the pure calculus.
|
|
2
|
+
|
|
3
|
+
This is the soundness certificate the specializer needs. ``TYPABLE`` consumes a quoted term (the
|
|
4
|
+
``QVar``/``QLam``/``QApp`` Scott value ``quote`` produces) and returns a Church boolean: whether the
|
|
5
|
+
term is simply typable, which is a sound certificate of strong normalization (so the strict
|
|
6
|
+
call-by-value runtime is safe). The Python ``_specialize._Inference`` is the specification this
|
|
7
|
+
ports and the test oracle it is checked against; it is not on the compile path.
|
|
8
|
+
|
|
9
|
+
This module is pure lambda calculus: every top-level binding is a ``Builder`` (a ``@curry``-decorated
|
|
10
|
+
``def`` IS a Builder). The Python-side verdict readers (``is_typable_lambda``, ``typable_bu_lambda``)
|
|
11
|
+
live at the boundary (``_specialize``).
|
|
12
|
+
|
|
13
|
+
The encoding is monomorphic algorithm-W (one fresh monotype per binder, no generalization), threading
|
|
14
|
+
a state of ``(next-fresh-id, substitution, failed-flag)`` purely:
|
|
15
|
+
|
|
16
|
+
* Types are a two-constructor Scott value: ``TVAR id`` (``id`` a BinNat, so allocating and comparing
|
|
17
|
+
type variables is O(log id)) and ``TARROW l r``.
|
|
18
|
+
* The substitution is a function ``id -> Option Type`` (the empty map is ``lambda id. NONE``; extension
|
|
19
|
+
shadows by id-equality), so ``resolve`` follows the chain and ``occurs`` walks the resolved type.
|
|
20
|
+
* ``unify`` threads ``(substitution, failed)`` and sets ``failed`` when the occurs check fires, which is
|
|
21
|
+
exactly why the self-application ``x x`` (constraint ``alpha = alpha -> beta``) is untypable.
|
|
22
|
+
* ``infer`` threads the whole state and returns ``(state, type)``; a binder extends the typing context
|
|
23
|
+
(a Scott list indexed by de Bruijn index) with a fresh monotype. It short-circuits once failed, so an
|
|
24
|
+
untypable term is rejected as soon as the first occurs check fires.
|
|
25
|
+
|
|
26
|
+
Termination: ``infer`` recurses structurally on the term; the occurs check keeps the substitution
|
|
27
|
+
acyclic, so ``resolve``/``occurs``/``unify`` recurse on finite type trees. The verdict is therefore a
|
|
28
|
+
normal-form Church boolean.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
from tablambda._binnat import BIN_ADD, BIN_EQUAL, BIN_SUCC, BIN_ZERO
|
|
34
|
+
from tablambda._dsl import Builder, app, lam
|
|
35
|
+
from tablambda._prelude import FALSE, IS_ZERO, OR, PRED, TRUE, Y
|
|
36
|
+
from tablambda._sugar import ap, let, pair, split_pair, split_quad, split_triple
|
|
37
|
+
|
|
38
|
+
# --- booleans -------------------------------------------------------------------------------------
|
|
39
|
+
_NOT: Builder = lam(lambda boolean: ap(boolean, FALSE, TRUE))
|
|
40
|
+
|
|
41
|
+
# Option: SOME carries a value, NONE is empty; consumed as ``option some_handler none_value``.
|
|
42
|
+
_SOME: Builder = lam(lambda value: lam(lambda some_handler: lam(lambda none_value: app(some_handler, value))))
|
|
43
|
+
_NONE: Builder = lam(lambda some_handler: lam(lambda none_value: none_value))
|
|
44
|
+
|
|
45
|
+
# Scott list: consumed as ``list nil_value cons_handler``.
|
|
46
|
+
_NIL: Builder = lam(lambda nil_value: lam(lambda cons_handler: nil_value))
|
|
47
|
+
_CONS: Builder = lam(lambda head: lam(lambda tail: lam(lambda nil_value: lam(lambda cons_handler: ap(cons_handler, head, tail)))))
|
|
48
|
+
|
|
49
|
+
# --- types: TVAR id | TARROW left right (consumed as ``type var_handler arrow_handler``) ----------
|
|
50
|
+
_TVAR: Builder = lam(lambda identifier: lam(lambda var_handler: lam(lambda arrow_handler: app(var_handler, identifier))))
|
|
51
|
+
_TARROW: Builder = lam(lambda left: lam(lambda right: lam(lambda var_handler: lam(lambda arrow_handler: ap(arrow_handler, left, right)))))
|
|
52
|
+
|
|
53
|
+
# --- substitution: a function id -> Option Type --------------------------------------------------
|
|
54
|
+
_EMPTY_SUBST: Builder = lam(lambda identifier: _NONE)
|
|
55
|
+
# extend subst id type = a new map shadowing ``id`` with ``SOME type``.
|
|
56
|
+
_EXTEND: Builder = lam(lambda subst: lam(lambda identifier: lam(lambda bound: lam(lambda lookup: ap(
|
|
57
|
+
ap(BIN_EQUAL, lookup, identifier),
|
|
58
|
+
app(_SOME, bound),
|
|
59
|
+
app(subst, lookup),
|
|
60
|
+
)))))
|
|
61
|
+
|
|
62
|
+
# resolve subst type: follow the substitution chain to the representative type.
|
|
63
|
+
_RESOLVE: Builder = app(Y, lam(lambda self_recursion: lam(lambda subst: lam(lambda type_: ap(
|
|
64
|
+
type_,
|
|
65
|
+
lam(lambda identifier: ap(
|
|
66
|
+
app(subst, identifier),
|
|
67
|
+
lam(lambda found: ap(self_recursion, subst, found)),
|
|
68
|
+
app(_TVAR, identifier),
|
|
69
|
+
)),
|
|
70
|
+
lam(lambda left: lam(lambda right: ap(_TARROW, left, right))),
|
|
71
|
+
)))))
|
|
72
|
+
|
|
73
|
+
# occurs subst id type: whether ``id`` occurs in the resolved ``type`` (the occurs check).
|
|
74
|
+
_OCCURS: Builder = app(Y, lam(lambda self_recursion: lam(lambda subst: lam(lambda identifier: lam(lambda type_: let(
|
|
75
|
+
ap(_RESOLVE, subst, type_),
|
|
76
|
+
lambda resolved: ap(
|
|
77
|
+
resolved,
|
|
78
|
+
lam(lambda other: ap(BIN_EQUAL, other, identifier)),
|
|
79
|
+
lam(lambda left: lam(lambda right: ap(
|
|
80
|
+
OR,
|
|
81
|
+
ap(self_recursion, subst, identifier, left),
|
|
82
|
+
ap(self_recursion, subst, identifier, right),
|
|
83
|
+
))),
|
|
84
|
+
),
|
|
85
|
+
))))))
|
|
86
|
+
|
|
87
|
+
# bind subst id type: if the occurs check fires, fail; else extend the substitution. The result is a
|
|
88
|
+
# pair (substitution, failed).
|
|
89
|
+
_BIND: Builder = lam(lambda subst: lam(lambda identifier: lam(lambda bound: ap(
|
|
90
|
+
ap(_OCCURS, subst, identifier, bound),
|
|
91
|
+
pair(subst, TRUE),
|
|
92
|
+
pair(ap(_EXTEND, subst, identifier, bound), FALSE),
|
|
93
|
+
))))
|
|
94
|
+
|
|
95
|
+
# unify state a b, with state = (substitution, failed): unify the two types, threading the state.
|
|
96
|
+
# Short-circuits when already failed; otherwise resolves both sides and matches the four shape cases:
|
|
97
|
+
# var/var (equal: nothing; else bind), var/arrow and arrow/var (bind after occurs check), arrow/arrow
|
|
98
|
+
# (unify the components left to right).
|
|
99
|
+
_UNIFY: Builder = app(Y, lam(lambda self_recursion: lam(lambda state: lam(lambda left_type: lam(lambda right_type: split_pair(
|
|
100
|
+
state,
|
|
101
|
+
lambda subst, failed: ap(
|
|
102
|
+
failed,
|
|
103
|
+
state,
|
|
104
|
+
let(ap(_RESOLVE, subst, left_type), lambda left: let(ap(_RESOLVE, subst, right_type), lambda right: ap(
|
|
105
|
+
left,
|
|
106
|
+
lam(lambda left_id: ap(
|
|
107
|
+
right,
|
|
108
|
+
lam(lambda right_id: ap(ap(BIN_EQUAL, left_id, right_id), state, ap(_BIND, subst, left_id, right))),
|
|
109
|
+
lam(lambda right_left: lam(lambda right_right: ap(_BIND, subst, left_id, right))),
|
|
110
|
+
)),
|
|
111
|
+
lam(lambda left_left: lam(lambda left_right: ap(
|
|
112
|
+
right,
|
|
113
|
+
lam(lambda right_id: ap(_BIND, subst, right_id, left)),
|
|
114
|
+
lam(lambda right_left: lam(lambda right_right: ap(
|
|
115
|
+
self_recursion,
|
|
116
|
+
ap(self_recursion, state, left_left, right_left),
|
|
117
|
+
left_right,
|
|
118
|
+
right_right,
|
|
119
|
+
))),
|
|
120
|
+
))),
|
|
121
|
+
))),
|
|
122
|
+
),
|
|
123
|
+
))))))
|
|
124
|
+
|
|
125
|
+
# --- inference state: (next-fresh-id, (substitution, failed)) ------------------------------------
|
|
126
|
+
# The fresh-id counter is a BinNat, so allocating and comparing type variables is O(log id).
|
|
127
|
+
_INITIAL_STATE: Builder = pair(BIN_ZERO, pair(_EMPTY_SUBST, FALSE))
|
|
128
|
+
|
|
129
|
+
# fresh state = ((next+1, subst, failed), TVAR next): the new state and the fresh type variable.
|
|
130
|
+
_FRESH: Builder = lam(lambda state: split_triple(
|
|
131
|
+
state,
|
|
132
|
+
lambda next_id, subst, failed: pair(
|
|
133
|
+
pair(app(BIN_SUCC, next_id), pair(subst, failed)),
|
|
134
|
+
app(_TVAR, next_id),
|
|
135
|
+
),
|
|
136
|
+
))
|
|
137
|
+
|
|
138
|
+
# unify_state state a b: apply unify to the (substitution, failed) part of the inference state.
|
|
139
|
+
_UNIFY_STATE: Builder = lam(lambda state: lam(lambda left_type: lam(lambda right_type: split_triple(
|
|
140
|
+
state,
|
|
141
|
+
lambda next_id, subst, failed: split_pair(
|
|
142
|
+
ap(_UNIFY, pair(subst, failed), left_type, right_type),
|
|
143
|
+
lambda new_subst, new_failed: pair(next_id, pair(new_subst, new_failed)),
|
|
144
|
+
),
|
|
145
|
+
))))
|
|
146
|
+
|
|
147
|
+
# lookup context index: the type at de Bruijn ``index`` in the context, as an Option.
|
|
148
|
+
_LOOKUP: Builder = app(Y, lam(lambda self_recursion: lam(lambda context: lam(lambda index: ap(
|
|
149
|
+
context,
|
|
150
|
+
_NONE,
|
|
151
|
+
lam(lambda head: lam(lambda tail: ap(
|
|
152
|
+
app(IS_ZERO, index),
|
|
153
|
+
app(_SOME, head),
|
|
154
|
+
ap(self_recursion, tail, app(PRED, index)),
|
|
155
|
+
))),
|
|
156
|
+
)))))
|
|
157
|
+
|
|
158
|
+
# infer state context node: infer the node's type, threading the state; returns (state, type).
|
|
159
|
+
# Once the state has failed (an occurs check fired), inference short-circuits: it returns a dummy type
|
|
160
|
+
# without recursing, so an untypable term (the compiler's Y, factorial, ...) is rejected as soon as the
|
|
161
|
+
# first self-application fails rather than building the whole constraint tree. This mirrors the Python
|
|
162
|
+
# ``_Inference.infer`` early return and is what keeps the certificate fast on the large untypable terms.
|
|
163
|
+
_INFER: Builder = app(Y, lam(lambda self_recursion: lam(lambda state: lam(lambda context: lam(lambda node: split_triple(
|
|
164
|
+
state,
|
|
165
|
+
lambda next_id, subst, failed: ap(
|
|
166
|
+
failed,
|
|
167
|
+
pair(state, app(_TVAR, next_id)), # already failed: short-circuit with a dummy type
|
|
168
|
+
ap(
|
|
169
|
+
node,
|
|
170
|
+
lam(lambda index: ap( # QVar index
|
|
171
|
+
ap(_LOOKUP, context, index),
|
|
172
|
+
lam(lambda found: pair(state, found)), # bound: its context type
|
|
173
|
+
app(_FRESH, state), # free: a fresh type variable
|
|
174
|
+
)),
|
|
175
|
+
lam(lambda body: split_pair( # QLam body
|
|
176
|
+
app(_FRESH, state),
|
|
177
|
+
lambda state_after_fresh, parameter: split_pair(
|
|
178
|
+
ap(self_recursion, state_after_fresh, ap(_CONS, parameter, context), body),
|
|
179
|
+
lambda state_after_body, result: pair(state_after_body, ap(_TARROW, parameter, result)),
|
|
180
|
+
),
|
|
181
|
+
)),
|
|
182
|
+
lam(lambda function: lam(lambda argument: split_pair( # QApp function argument
|
|
183
|
+
ap(self_recursion, state, context, function),
|
|
184
|
+
lambda state_after_function, function_type: split_pair(
|
|
185
|
+
ap(self_recursion, state_after_function, context, argument),
|
|
186
|
+
lambda state_after_argument, argument_type: split_pair(
|
|
187
|
+
app(_FRESH, state_after_argument),
|
|
188
|
+
lambda state_after_fresh, result: pair(
|
|
189
|
+
ap(_UNIFY_STATE, state_after_fresh, function_type, ap(_TARROW, argument_type, result)),
|
|
190
|
+
result,
|
|
191
|
+
),
|
|
192
|
+
),
|
|
193
|
+
),
|
|
194
|
+
))),
|
|
195
|
+
),
|
|
196
|
+
),
|
|
197
|
+
))))))
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
# TYPABLE quoted = run inference from the initial state on the empty context, read the failed flag.
|
|
201
|
+
# A closed term is simply typable iff inference does not fail (no occurs-check violation).
|
|
202
|
+
TYPABLE: Builder = lam(lambda quoted: split_pair(
|
|
203
|
+
ap(_INFER, _INITIAL_STATE, _NIL, quoted),
|
|
204
|
+
lambda final_state, _type: split_triple(
|
|
205
|
+
final_state,
|
|
206
|
+
lambda next_id, subst, failed: app(_NOT, failed),
|
|
207
|
+
),
|
|
208
|
+
))
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# === Bottom-up principal typing: one path-free fold the interpreter tables per distinct sub-term ===
|
|
212
|
+
# ``TYPABLE`` above threads a fresh-id/substitution state, so the interpreter cannot share its
|
|
213
|
+
# per-sub-term inference (the state differs at every position) and the substitution is an O(chain)
|
|
214
|
+
# function. ``PRINCIPAL`` is PATH-FREE -- it takes only the node -- so ``app(PRINCIPAL, sub)`` is the
|
|
215
|
+
# same interned node wherever ``sub`` occurs and the interpreter tables it ONCE per distinct sub-term
|
|
216
|
+
# (the compiler's combinators reuse sub-combinators heavily, so this is the win). Each ``App`` unifies
|
|
217
|
+
# in a FRESH LOCAL substitution, resolved and discarded, so there is no global chain. Type-variable
|
|
218
|
+
# ids are BinNats (O(log) equality); de Bruijn indices stay Church (small, bounded by depth) and are
|
|
219
|
+
# converted to BinNat only where they seed a type-variable id.
|
|
220
|
+
#
|
|
221
|
+
# A result is ``(next-fresh-id, context, type, failed)``: ``context`` is a Scott list of types indexed
|
|
222
|
+
# by de Bruijn index (a fresh type per binder so siblings constrain them independently); ``type`` is
|
|
223
|
+
# the sub-term's type with the local substitution applied; ``next-fresh-id`` (a BinNat) bounds the
|
|
224
|
+
# type-variable ids used, so a sibling is renamed apart by adding it; ``failed`` is the occurs-check
|
|
225
|
+
# verdict for the whole sub-tree.
|
|
226
|
+
|
|
227
|
+
# Convert a Church numeral (a de Bruijn index from ``quote``) to a BinNat type-variable id.
|
|
228
|
+
_CHURCH_TO_BINNAT: Builder = lam(lambda church_value: ap(church_value, BIN_SUCC, BIN_ZERO))
|
|
229
|
+
|
|
230
|
+
# build_vars count = [TVAR 0, TVAR 1, ..., TVAR (count-1)]: the fresh context for a variable at de
|
|
231
|
+
# Bruijn index count-1, one distinct fresh type per enclosing binder (BinNat ids).
|
|
232
|
+
_BUILD_VARS_GO: Builder = app(Y, lam(lambda self_recursion: lam(lambda current: lam(lambda count: ap(
|
|
233
|
+
ap(BIN_EQUAL, current, count),
|
|
234
|
+
_NIL,
|
|
235
|
+
ap(_CONS, app(_TVAR, current), ap(self_recursion, app(BIN_SUCC, current), count)),
|
|
236
|
+
)))))
|
|
237
|
+
|
|
238
|
+
_BUILD_VARS: Builder = lam(lambda count: ap(_BUILD_VARS_GO, BIN_ZERO, count))
|
|
239
|
+
|
|
240
|
+
# shift_type offset type: add ``offset`` to every type-variable id (rename a whole type apart).
|
|
241
|
+
_SHIFT_TYPE: Builder = app(Y, lam(lambda self_recursion: lam(lambda offset: lam(lambda type_: ap(
|
|
242
|
+
type_,
|
|
243
|
+
lam(lambda identifier: app(_TVAR, ap(BIN_ADD, offset, identifier))),
|
|
244
|
+
lam(lambda left: lam(lambda right: ap(
|
|
245
|
+
_TARROW,
|
|
246
|
+
ap(self_recursion, offset, left), ap(self_recursion, offset, right),
|
|
247
|
+
))),
|
|
248
|
+
)))))
|
|
249
|
+
|
|
250
|
+
# shift_context offset context: rename every type in a context apart by ``offset``.
|
|
251
|
+
_SHIFT_CONTEXT: Builder = app(Y, lam(lambda self_recursion: lam(lambda offset: lam(lambda context: ap(
|
|
252
|
+
context,
|
|
253
|
+
_NIL,
|
|
254
|
+
lam(lambda head: lam(lambda tail: ap(
|
|
255
|
+
_CONS,
|
|
256
|
+
ap(_SHIFT_TYPE, offset, head), ap(self_recursion, offset, tail),
|
|
257
|
+
))),
|
|
258
|
+
)))))
|
|
259
|
+
|
|
260
|
+
# apply_subst subst type: resolve ``type`` deeply, so the result carries no residual substitution.
|
|
261
|
+
_APPLY_SUBST: Builder = app(Y, lam(lambda self_recursion: lam(lambda subst: lam(lambda type_: ap(
|
|
262
|
+
ap(_RESOLVE, subst, type_),
|
|
263
|
+
lam(lambda identifier: app(_TVAR, identifier)),
|
|
264
|
+
lam(lambda left: lam(lambda right: ap(
|
|
265
|
+
_TARROW,
|
|
266
|
+
ap(self_recursion, subst, left), ap(self_recursion, subst, right),
|
|
267
|
+
))),
|
|
268
|
+
)))))
|
|
269
|
+
|
|
270
|
+
_APPLY_SUBST_CONTEXT: Builder = app(Y, lam(lambda self_recursion: lam(lambda subst: lam(lambda context: ap(
|
|
271
|
+
context,
|
|
272
|
+
_NIL,
|
|
273
|
+
lam(lambda head: lam(lambda tail: ap(
|
|
274
|
+
_CONS,
|
|
275
|
+
ap(_APPLY_SUBST, subst, head), ap(self_recursion, subst, tail),
|
|
276
|
+
))),
|
|
277
|
+
)))))
|
|
278
|
+
|
|
279
|
+
# merge state a b, state = (subst, failed): unify the shared prefix of two contexts (same de Bruijn
|
|
280
|
+
# indices) and keep the tail of the longer; returns (state, merged-context).
|
|
281
|
+
_MERGE: Builder = app(Y, lam(lambda self_recursion: lam(lambda state: lam(lambda a: lam(lambda b: ap(
|
|
282
|
+
a,
|
|
283
|
+
pair(state, b), # a is nil: the merge is b
|
|
284
|
+
lam(lambda head_a: lam(lambda tail_a: ap(
|
|
285
|
+
b,
|
|
286
|
+
pair(state, ap(_CONS, head_a, tail_a)), # b is nil: the merge is a
|
|
287
|
+
lam(lambda head_b: lam(lambda tail_b: split_pair(
|
|
288
|
+
ap(self_recursion, ap(_UNIFY, state, head_a, head_b), tail_a, tail_b),
|
|
289
|
+
lambda merged_state, merged_tail: pair(merged_state, ap(_CONS, head_a, merged_tail)),
|
|
290
|
+
))),
|
|
291
|
+
))),
|
|
292
|
+
))))))
|
|
293
|
+
|
|
294
|
+
_INITIAL_PAIR: Builder = pair(_EMPTY_SUBST, FALSE)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
# principal node: the bottom-up principal typing of a quoted term, the path-free fold described above.
|
|
298
|
+
PRINCIPAL: Builder = app(Y, lam(lambda self_recursion: lam(lambda node: ap(
|
|
299
|
+
node,
|
|
300
|
+
# QVar index: context [TVAR 0 .. TVAR index], type TVAR index (ids as BinNat).
|
|
301
|
+
lam(lambda index: let(app(_CHURCH_TO_BINNAT, index), lambda binnat_index: pair(
|
|
302
|
+
app(BIN_SUCC, binnat_index),
|
|
303
|
+
pair(
|
|
304
|
+
app(_BUILD_VARS, app(BIN_SUCC, binnat_index)),
|
|
305
|
+
pair(app(_TVAR, binnat_index), FALSE),
|
|
306
|
+
),
|
|
307
|
+
))),
|
|
308
|
+
# QLam body: discharge de Bruijn index 0 (the binder's type) from the body's context.
|
|
309
|
+
lam(lambda body: split_quad(
|
|
310
|
+
app(self_recursion, body),
|
|
311
|
+
lambda next_body, context_body, type_body, failed_body: ap(
|
|
312
|
+
context_body,
|
|
313
|
+
# body uses no enclosing binder: the parameter is a fresh, unconstrained type.
|
|
314
|
+
pair(
|
|
315
|
+
app(BIN_SUCC, next_body),
|
|
316
|
+
pair(_NIL, pair(ap(_TARROW, app(_TVAR, next_body), type_body), failed_body)),
|
|
317
|
+
),
|
|
318
|
+
lam(lambda parameter: lam(lambda rest: pair(
|
|
319
|
+
next_body,
|
|
320
|
+
pair(rest, pair(ap(_TARROW, parameter, type_body), failed_body)),
|
|
321
|
+
))),
|
|
322
|
+
),
|
|
323
|
+
)),
|
|
324
|
+
# QApp function argument: rename the argument's type-var band apart, merge the shared context,
|
|
325
|
+
# then unify the function's type with (argument-type -> fresh result).
|
|
326
|
+
lam(lambda function: lam(lambda argument: split_quad(
|
|
327
|
+
app(self_recursion, function),
|
|
328
|
+
lambda next_f, context_f, type_f, failed_f: split_quad(
|
|
329
|
+
app(self_recursion, argument),
|
|
330
|
+
lambda next_a, context_a, type_a, failed_a: ap(
|
|
331
|
+
ap(OR, failed_f, failed_a),
|
|
332
|
+
pair(
|
|
333
|
+
app(BIN_SUCC, ap(BIN_ADD, next_f, next_a)),
|
|
334
|
+
pair(_NIL, pair(app(_TVAR, BIN_ZERO), TRUE)),
|
|
335
|
+
),
|
|
336
|
+
let(ap(BIN_ADD, next_f, next_a), lambda total: let(
|
|
337
|
+
ap(_SHIFT_CONTEXT, next_f, context_a), lambda context_a_shifted: let(
|
|
338
|
+
ap(_SHIFT_TYPE, next_f, type_a), lambda type_a_shifted: let(
|
|
339
|
+
app(_TVAR, total), lambda result_type: split_pair(
|
|
340
|
+
ap(_MERGE, _INITIAL_PAIR, context_f, context_a_shifted),
|
|
341
|
+
lambda merged_state, merged_context: split_pair(
|
|
342
|
+
ap(_UNIFY, merged_state, type_f, ap(_TARROW, type_a_shifted, result_type)),
|
|
343
|
+
lambda final_subst, final_failed: ap(
|
|
344
|
+
final_failed,
|
|
345
|
+
pair(
|
|
346
|
+
app(BIN_SUCC, total),
|
|
347
|
+
pair(_NIL, pair(app(_TVAR, BIN_ZERO), TRUE)),
|
|
348
|
+
),
|
|
349
|
+
pair(
|
|
350
|
+
app(BIN_SUCC, total),
|
|
351
|
+
pair(
|
|
352
|
+
ap(_APPLY_SUBST_CONTEXT, final_subst, merged_context),
|
|
353
|
+
pair(ap(_APPLY_SUBST, final_subst, result_type), FALSE),
|
|
354
|
+
),
|
|
355
|
+
),
|
|
356
|
+
),
|
|
357
|
+
),
|
|
358
|
+
)))),
|
|
359
|
+
),
|
|
360
|
+
),
|
|
361
|
+
),
|
|
362
|
+
))),
|
|
363
|
+
))))
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
# TYPABLE_BU quoted: simply typable iff the bottom-up principal typing has no occurs-check failure.
|
|
367
|
+
TYPABLE_BU: Builder = lam(lambda quoted: split_quad(
|
|
368
|
+
app(PRINCIPAL, quoted),
|
|
369
|
+
lambda next_id, context, type_, failed: app(_NOT, failed),
|
|
370
|
+
))
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tablambda
|
|
3
|
+
Version: 0.6.0.post30.dev0
|
|
4
|
+
Summary: A pure lambda-calculus interpreter that applies tabling to weak-head reduction, folding self-referential terms into finite cyclic graphs
|
|
5
|
+
Project-URL: Repository, https://github.com/Atry/MIXINv2
|
|
6
|
+
Author-email: "Yang, Bo" <yang-bo@yang-bo.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Requires-Dist: fixpoints
|
|
14
|
+
Requires-Dist: typing-extensions>=4.1.0
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# tablambda
|
|
18
|
+
|
|
19
|
+
[](https://github.com/Atry/tablambda/actions/workflows/ci.yml)
|
|
20
|
+
[](https://pypi.org/project/tablambda/)
|
|
21
|
+
[](https://pypi.org/project/tablambda/)
|
|
22
|
+

|
|
23
|
+
|
|
24
|
+
A pure lambda-calculus interpreter and compiler, realizing the semantics of the
|
|
25
|
+
paper `paper/tablambda.tex` and depending on `fixpoints`.
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
pip install tablambda
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
You write ordinary pure lambda terms, and the interpreter gives them powers a
|
|
32
|
+
pure language normally lacks:
|
|
33
|
+
|
|
34
|
+
- Cyclic and infinite data structures, built and transformed directly, with no
|
|
35
|
+
`letrec`, no added recursion construct, and no mutable references.
|
|
36
|
+
- Automatic memoization and dynamic programming: repeated subproblems are shared
|
|
37
|
+
for you, with no cache written by hand.
|
|
38
|
+
- A diverging loop is detected and returns a meaningless value in finite time
|
|
39
|
+
instead of hanging.
|
|
40
|
+
|
|
41
|
+
The companion compiler turns a term into a standalone Python module, so a program
|
|
42
|
+
written once as a lambda term can also run as compiled code.
|
|
43
|
+
|
|
44
|
+
No parser is provided: terms are built directly in Python, with a small prelude
|
|
45
|
+
of the usual combinators, Church numerals, and Scott-encoded lists.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
tablambda/__init__.py,sha256=a0mKmqAG6xvxN9qmG-3XhsN-YMOvs4ufVOW57Lpr73c,144
|
|
2
|
+
tablambda/_analysis.py,sha256=sH06hqckr4KdDKReN_EFpxXosweOYl76Qq8KPbjfkSI,5224
|
|
3
|
+
tablambda/_ast.py,sha256=ltFf-ROqWgspDRiLXc0Z2FOc7wpsLPKKgMIchJlfojE,11860
|
|
4
|
+
tablambda/_binnat.py,sha256=TMKbB5Ct5aRYwc7tC1dGsuSFSoQy1UqL_FpubeemF04,6589
|
|
5
|
+
tablambda/_codec.py,sha256=0OSKOJV2hocZMmn48TLvjqbMnZ_MJ1wOJcwlKIC0chQ,5979
|
|
6
|
+
tablambda/_compiler_artifact.py,sha256=j9JWNDt1ThtOMz1JTLpD6KkTaOkw3QwUV9jA2psom3o,1828
|
|
7
|
+
tablambda/_defun_codegen.py,sha256=vSgk31P_AgbUI506RnLV23RsJA2Hk9j_Y-zhGxOfNh4,13147
|
|
8
|
+
tablambda/_defun_runtime.py,sha256=yXgnlYkF3mPttXENCzzxSqNuE1XD3IPPAdTXnl2zt18,7947
|
|
9
|
+
tablambda/_defunctionalize.py,sha256=M1jJwaoG_vQAEkrXcVfYlzlnvGXrIvsP02B96B82Kvw,17708
|
|
10
|
+
tablambda/_dsl.py,sha256=J0mTuyL-qPQgHJxvD9R1SCc0M8imODnBvotTORywawM,5901
|
|
11
|
+
tablambda/_hoas_latex.py,sha256=l5AkiODG0CRRqFcmRUnYuTlWtmNzvalwpE8gRTnjgPw,6205
|
|
12
|
+
tablambda/_latex.py,sha256=GetMAkyNLAx_C67qYkC7mMaq9frV28yZ9TpfG7zGQ-k,2271
|
|
13
|
+
tablambda/_prelude.py,sha256=TurPPOAP31JfCLGwRYzps36fQ9FayBrrJBajeqRSN50,6694
|
|
14
|
+
tablambda/_pyast.py,sha256=FBxW-2-QrJ4tqqKuHKT1I-nbGum5a83-eVqiLXSGkTQ,18423
|
|
15
|
+
tablambda/_pybuild.py,sha256=cxfv46W5kLaAMSBODL-EV8705RAxfobfNHdBYDwIDSg,12933
|
|
16
|
+
tablambda/_reduce.py,sha256=ZJH_MMiMkZ3aeJGux_l0jEU9eEnd4PZpOcnk6yjcTJM,7794
|
|
17
|
+
tablambda/_shape.py,sha256=lk6GrUzCLXXDbndMCFErXVJk-ww-4z4ZCwkQsa4Js5U,4863
|
|
18
|
+
tablambda/_sugar.py,sha256=n20ZCK0pCxQYLyptM_KWB7GFm0bui3EOs3fhJVCL6AE,2804
|
|
19
|
+
tablambda/_typecheck.py,sha256=ki_Av6BlXOd3SBsjMxIGcyQS7x0g3v9nDdsDmgeUyBc,18451
|
|
20
|
+
tablambda/_generated/.gitattributes,sha256=6IjxpkFnEEkCZSuax4ffMoCQFfy1aR28A4Eqaa4kwIo,319
|
|
21
|
+
tablambda/_generated/__init__.py,sha256=_XA-qAGrK7esywkAOlTnKDF01A8c3MVFCK4FToC8Y6U,80
|
|
22
|
+
tablambda/_generated/_generated_defun_compiler_py311.py,sha256=cp6l4H9Od36Z-Xa_KHUJj4XL0dkXfFmVYSDCcwG1Rpo,230173
|
|
23
|
+
tablambda/_generated/_generated_defun_compiler_py312.py,sha256=fc0y4kRMGzYCNt1yK71VoC7rYNh2wGaCstJTvXbiCL8,230233
|
|
24
|
+
tablambda/_generated/_generated_defun_compiler_py313.py,sha256=QOUagbaoMQmXSDvEgzOwKoNZmbhWU9xE1Ex69piGjfg,230233
|
|
25
|
+
tablambda-0.6.0.post30.dev0.dist-info/METADATA,sha256=f3ifx7gyFbamqrir8CuU14CTkl8vUUd5oS4mq-0G_rc,2016
|
|
26
|
+
tablambda-0.6.0.post30.dev0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
27
|
+
tablambda-0.6.0.post30.dev0.dist-info/entry_points.txt,sha256=M_gCK9oalSbNRaz669K_caYr8a4riwXRT4VdkF9Ub9Q,79
|
|
28
|
+
tablambda-0.6.0.post30.dev0.dist-info/RECORD,,
|