tablambda 0.6.0.post30.dev0__tar.gz
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-0.6.0.post30.dev0/.gitignore +247 -0
- tablambda-0.6.0.post30.dev0/PKG-INFO +45 -0
- tablambda-0.6.0.post30.dev0/README.md +29 -0
- tablambda-0.6.0.post30.dev0/pyproject.toml +43 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/__init__.py +1 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_analysis.py +92 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_ast.py +289 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_binnat.py +170 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_codec.py +147 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_compiler_artifact.py +50 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_defun_codegen.py +324 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_defun_runtime.py +207 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_defunctionalize.py +455 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_dsl.py +148 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_generated/.gitattributes +5 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_generated/__init__.py +1 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_generated/_generated_defun_compiler_py311.py +7579 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_generated/_generated_defun_compiler_py312.py +7579 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_generated/_generated_defun_compiler_py313.py +7579 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_hoas_latex.py +144 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_latex.py +56 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_prelude.py +163 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_pyast.py +416 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_pybuild.py +315 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_reduce.py +145 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_shape.py +129 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_sugar.py +74 -0
- tablambda-0.6.0.post30.dev0/src/tablambda/_typecheck.py +370 -0
- tablambda-0.6.0.post30.dev0/tests/__snapshots__/test_binnat.ambr +8 -0
- tablambda-0.6.0.post30.dev0/tests/__snapshots__/test_defunctionalize.ambr +76 -0
- tablambda-0.6.0.post30.dev0/tests/test_arithmetic.py +82 -0
- tablambda-0.6.0.post30.dev0/tests/test_binnat.py +81 -0
- tablambda-0.6.0.post30.dev0/tests/test_defunctionalize.py +279 -0
- tablambda-0.6.0.post30.dev0/tests/test_hoas_latex.py +30 -0
- tablambda-0.6.0.post30.dev0/tests/test_pyast.py +69 -0
- tablambda-0.6.0.post30.dev0/tests/test_pybuild.py +108 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
**/docs/_build/
|
|
73
|
+
**/docs/api/
|
|
74
|
+
|
|
75
|
+
# PyBuilder
|
|
76
|
+
.pybuilder/
|
|
77
|
+
target/
|
|
78
|
+
|
|
79
|
+
# Jupyter Notebook
|
|
80
|
+
.ipynb_checkpoints
|
|
81
|
+
.jupyter_ystore.db
|
|
82
|
+
|
|
83
|
+
# IPython
|
|
84
|
+
profile_default/
|
|
85
|
+
ipython_config.py
|
|
86
|
+
|
|
87
|
+
# pyenv
|
|
88
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
89
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
90
|
+
# .python-version
|
|
91
|
+
|
|
92
|
+
# pipenv
|
|
93
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
94
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
95
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
96
|
+
# install all needed dependencies.
|
|
97
|
+
#Pipfile.lock
|
|
98
|
+
|
|
99
|
+
# UV
|
|
100
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
101
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
102
|
+
# commonly ignored for libraries.
|
|
103
|
+
#uv.lock
|
|
104
|
+
|
|
105
|
+
# poetry
|
|
106
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
107
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
108
|
+
# commonly ignored for libraries.
|
|
109
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
110
|
+
#poetry.lock
|
|
111
|
+
#poetry.toml
|
|
112
|
+
|
|
113
|
+
# pdm
|
|
114
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
115
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
116
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
117
|
+
#pdm.lock
|
|
118
|
+
#pdm.toml
|
|
119
|
+
.pdm-python
|
|
120
|
+
.pdm-build/
|
|
121
|
+
|
|
122
|
+
# pixi
|
|
123
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
124
|
+
#pixi.lock
|
|
125
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
126
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
127
|
+
.pixi
|
|
128
|
+
|
|
129
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
130
|
+
__pypackages__/
|
|
131
|
+
|
|
132
|
+
# Celery stuff
|
|
133
|
+
celerybeat-schedule
|
|
134
|
+
celerybeat.pid
|
|
135
|
+
|
|
136
|
+
# SageMath parsed files
|
|
137
|
+
*.sage.py
|
|
138
|
+
|
|
139
|
+
# Environments
|
|
140
|
+
.env
|
|
141
|
+
.venv
|
|
142
|
+
env/
|
|
143
|
+
venv/
|
|
144
|
+
ENV/
|
|
145
|
+
env.bak/
|
|
146
|
+
venv.bak/
|
|
147
|
+
|
|
148
|
+
# Spyder project settings
|
|
149
|
+
.spyderproject
|
|
150
|
+
.spyproject
|
|
151
|
+
|
|
152
|
+
# Rope project settings
|
|
153
|
+
.ropeproject
|
|
154
|
+
|
|
155
|
+
# mkdocs documentation
|
|
156
|
+
/site
|
|
157
|
+
|
|
158
|
+
# mypy
|
|
159
|
+
.mypy_cache/
|
|
160
|
+
.dmypy.json
|
|
161
|
+
dmypy.json
|
|
162
|
+
|
|
163
|
+
# Pyre type checker
|
|
164
|
+
.pyre/
|
|
165
|
+
|
|
166
|
+
# pytype static type analyzer
|
|
167
|
+
.pytype/
|
|
168
|
+
|
|
169
|
+
# Cython debug symbols
|
|
170
|
+
cython_debug/
|
|
171
|
+
|
|
172
|
+
# PyCharm
|
|
173
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
174
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
175
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
176
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
177
|
+
#.idea/
|
|
178
|
+
|
|
179
|
+
# Abstra
|
|
180
|
+
# Abstra is an AI-powered process automation framework.
|
|
181
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
182
|
+
# Learn more at https://abstra.io/docs
|
|
183
|
+
.abstra/
|
|
184
|
+
|
|
185
|
+
# Visual Studio Code
|
|
186
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
187
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
188
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
189
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
190
|
+
# .vscode/
|
|
191
|
+
|
|
192
|
+
# Ruff stuff:
|
|
193
|
+
.ruff_cache/
|
|
194
|
+
|
|
195
|
+
# PyPI configuration file
|
|
196
|
+
.pypirc
|
|
197
|
+
|
|
198
|
+
# Cursor
|
|
199
|
+
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
|
|
200
|
+
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
|
201
|
+
# refer to https://docs.cursor.com/context/ignore-files
|
|
202
|
+
.cursorignore
|
|
203
|
+
.cursorindexingignore
|
|
204
|
+
|
|
205
|
+
# Marimo
|
|
206
|
+
marimo/_static/
|
|
207
|
+
marimo/_lsp/
|
|
208
|
+
__marimo__/
|
|
209
|
+
|
|
210
|
+
.direnv/
|
|
211
|
+
.devenv/
|
|
212
|
+
result
|
|
213
|
+
|
|
214
|
+
# devenv/nixago scaffolding regenerated by running `nix develop` in a paper subdirectory
|
|
215
|
+
# (the canonical copies live at the repository root)
|
|
216
|
+
/paper/.envrc
|
|
217
|
+
/paper/.gitattributes
|
|
218
|
+
/paper/.vscode/
|
|
219
|
+
|
|
220
|
+
# LaTeX
|
|
221
|
+
*.pdf
|
|
222
|
+
*.aux
|
|
223
|
+
*.fls
|
|
224
|
+
*.fdb_latexmk
|
|
225
|
+
*.synctex.gz
|
|
226
|
+
*.bbl
|
|
227
|
+
*.blg
|
|
228
|
+
*.out
|
|
229
|
+
*.dvi
|
|
230
|
+
*.xcp
|
|
231
|
+
|
|
232
|
+
# Local data
|
|
233
|
+
data/
|
|
234
|
+
trajectory/
|
|
235
|
+
experiment_results.db
|
|
236
|
+
|
|
237
|
+
.playwright-mcp/
|
|
238
|
+
*.local.*
|
|
239
|
+
.envrc.private
|
|
240
|
+
.pre-commit-config.yaml
|
|
241
|
+
|
|
242
|
+
# nixago: ignore-linked-files
|
|
243
|
+
/.vscode/extensions.json
|
|
244
|
+
|
|
245
|
+
arxiv-submission.tar.gz
|
|
246
|
+
node_modules/
|
|
247
|
+
comment.cut
|
|
@@ -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,29 @@
|
|
|
1
|
+
# tablambda
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Atry/tablambda/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/tablambda/)
|
|
5
|
+
[](https://pypi.org/project/tablambda/)
|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
A pure lambda-calculus interpreter and compiler, realizing the semantics of the
|
|
9
|
+
paper `paper/tablambda.tex` and depending on `fixpoints`.
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
pip install tablambda
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
You write ordinary pure lambda terms, and the interpreter gives them powers a
|
|
16
|
+
pure language normally lacks:
|
|
17
|
+
|
|
18
|
+
- Cyclic and infinite data structures, built and transformed directly, with no
|
|
19
|
+
`letrec`, no added recursion construct, and no mutable references.
|
|
20
|
+
- Automatic memoization and dynamic programming: repeated subproblems are shared
|
|
21
|
+
for you, with no cache written by hand.
|
|
22
|
+
- A diverging loop is detected and returns a meaningless value in finite time
|
|
23
|
+
instead of hanging.
|
|
24
|
+
|
|
25
|
+
The companion compiler turns a term into a standalone Python module, so a program
|
|
26
|
+
written once as a lambda term can also run as compiled code.
|
|
27
|
+
|
|
28
|
+
No parser is provided: terms are built directly in Python, with a small prelude
|
|
29
|
+
of the usual combinators, Church numerals, and Scott-encoded lists.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling", "uv-dynamic-versioning>=0.7.0", "editables"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[tool.hatch.version]
|
|
6
|
+
source = "uv-dynamic-versioning"
|
|
7
|
+
|
|
8
|
+
[tool.uv-dynamic-versioning]
|
|
9
|
+
vcs = "git"
|
|
10
|
+
style = "pep440"
|
|
11
|
+
bump = false
|
|
12
|
+
fallback-version = "0.0.0.dev0"
|
|
13
|
+
metadata = false
|
|
14
|
+
|
|
15
|
+
[project]
|
|
16
|
+
name = "tablambda"
|
|
17
|
+
dynamic = ["version"]
|
|
18
|
+
description = "A pure lambda-calculus interpreter that applies tabling to weak-head reduction, folding self-referential terms into finite cyclic graphs"
|
|
19
|
+
readme = "README.md"
|
|
20
|
+
license = "MIT"
|
|
21
|
+
requires-python = ">=3.11"
|
|
22
|
+
authors = [{ name = "Yang, Bo", email = "yang-bo@yang-bo.com" }]
|
|
23
|
+
classifiers = [
|
|
24
|
+
"Development Status :: 3 - Alpha",
|
|
25
|
+
"License :: OSI Approved :: MIT License",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Programming Language :: Python :: 3.14",
|
|
28
|
+
]
|
|
29
|
+
dependencies = ["fixpoints", "typing-extensions>=4.1.0"]
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
tablambda-regen-compiler = "tablambda._compiler_artifact:main"
|
|
33
|
+
|
|
34
|
+
[tool.uv]
|
|
35
|
+
sources = { fixpoints = { workspace = true } }
|
|
36
|
+
|
|
37
|
+
[tool.hatch.build.targets.wheel]
|
|
38
|
+
packages = ["src/tablambda"]
|
|
39
|
+
only-include = ["src/tablambda"]
|
|
40
|
+
sources = {"src" = ""}
|
|
41
|
+
|
|
42
|
+
[project.urls]
|
|
43
|
+
Repository = "https://github.com/Atry/MIXINv2"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""A pure lambda-calculus interpreter that applies tabling to weak-head reduction, folding self-referential terms into finite cyclic graphs."""
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Specialization analysis written in the lambda-calculus itself.
|
|
2
|
+
|
|
3
|
+
The analysis that decides which sub-terms to specialize is a pure lambda term, run by the
|
|
4
|
+
interpreter on the quoted program, so the calculus analyzes its own programs: a demonstration that
|
|
5
|
+
tabling-based reduction expresses program analysis, not only evaluation. This module holds the
|
|
6
|
+
closedness and depth measures; richer certificates (typability, fuel-bounded normalization) live in
|
|
7
|
+
``_typecheck`` and ``_reduce`` in the same style.
|
|
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 live at the boundary (``_specialize``).
|
|
11
|
+
|
|
12
|
+
``LOOSE_BOUND`` is a DEPTH-FREE closedness measure, so the interpreter's interning shares it across
|
|
13
|
+
every position: ``LOOSE_BOUND quoted`` takes no binder depth, so ``app(LOOSE_BOUND, sub)`` is the
|
|
14
|
+
SAME node for an interned sub-term and is tabled once; a whole-tree scan is then linear. It returns
|
|
15
|
+
the number of enclosing binders the sub-term needs (the de Bruijn ``loose_bound``): a variable needs
|
|
16
|
+
index+1, an abstraction discharges one (floored at zero by ``PRED``), an application needs the larger
|
|
17
|
+
of the two. A sub-term is closed exactly when it needs none (``IS_CLOSED``).
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from tablambda._dsl import Builder, app, curry, lam
|
|
23
|
+
from tablambda._prelude import IS_ZERO, PLUS, PRED, SUCC, Y
|
|
24
|
+
from tablambda._sugar import ap
|
|
25
|
+
|
|
26
|
+
# Church arithmetic for the measures (truncated subtraction gives the comparisons).
|
|
27
|
+
_SUBTRACT: Builder = lam(lambda a: lam(lambda b: app(app(b, PRED), a))) # a - b, floored at zero
|
|
28
|
+
_AT_MOST: Builder = lam(lambda a: lam(lambda b: app(IS_ZERO, ap(_SUBTRACT, a, b)))) # a <= b
|
|
29
|
+
_MAX: Builder = lam(lambda a: lam(lambda b: ap(_AT_MOST, a, b, b, a))) # a <= b ? b : a
|
|
30
|
+
|
|
31
|
+
LOOSE_BOUND: Builder = app(Y, lam(lambda self_recursion: lam(lambda quoted: ap(
|
|
32
|
+
quoted,
|
|
33
|
+
lam(lambda index: app(SUCC, index)), # QVar index: needs index+1 enclosing binders
|
|
34
|
+
lam(lambda body: app(PRED, app(self_recursion, body))), # QLam body: discharges one binder
|
|
35
|
+
lam(lambda function: lam(lambda argument: ap(
|
|
36
|
+
_MAX, app(self_recursion, function), app(self_recursion, argument),
|
|
37
|
+
))), # QApp f a: the larger of the two
|
|
38
|
+
))))
|
|
39
|
+
|
|
40
|
+
IS_CLOSED: Builder = lam(lambda quoted: app(IS_ZERO, app(LOOSE_BOUND, quoted))) # closed iff needs none
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# DEPTH: the nesting depth of a quoted term (a Church numeral), a cheap path-free measure the interner
|
|
44
|
+
# shares per distinct sub-term. It bounds the simple-typability check: running algorithm-W on a large
|
|
45
|
+
# (deep) closed combinator is expensive and the no-GC interner retains every reduction, so a specializer
|
|
46
|
+
# only certifies an island when the sub-term is shallow enough (``depth_at_most``), leaving a deep closed
|
|
47
|
+
# region reconstructed as an interpreted graph rather than flattened to a strict island. The bound only
|
|
48
|
+
# ever makes the certificate MORE conservative (fewer islands), never unsound.
|
|
49
|
+
DEPTH: Builder = app(Y, lam(lambda self_recursion: lam(lambda quoted: ap(
|
|
50
|
+
quoted,
|
|
51
|
+
lam(lambda index: lam(lambda s: lam(lambda z: z))), # QVar: a leaf (depth zero)
|
|
52
|
+
lam(lambda body: app(SUCC, app(self_recursion, body))), # QLam: one deeper
|
|
53
|
+
lam(lambda function: lam(lambda argument: app(SUCC, ap(
|
|
54
|
+
_MAX, app(self_recursion, function), app(self_recursion, argument),
|
|
55
|
+
)))), # QApp: one past the deeper side
|
|
56
|
+
))))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@curry
|
|
60
|
+
def depth_at_most(bound: Builder, quoted: Builder) -> Builder:
|
|
61
|
+
"""``DEPTH quoted <= bound`` (a Church boolean): the shallow-enough certificate."""
|
|
62
|
+
return ap(_AT_MOST, app(DEPTH, quoted), bound)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# NODE_COUNT: the number of Var/Lam/App nodes in a quoted term (a Church numeral), a path-free
|
|
66
|
+
# catamorphism the interner shares per distinct sub-term -- same shape as DEPTH but summing (PLUS) the
|
|
67
|
+
# children instead of taking their MAX. It bounds how big a region may be de-tabled (host-compiled to
|
|
68
|
+
# call-by-need): a small region (<= a bound) loses cross-location tabling at most a constant factor, never
|
|
69
|
+
# exponentially, so the local-call-by-need optimization stays bounded and measurable.
|
|
70
|
+
_ZERO: Builder = lam(lambda s: lam(lambda z: z)) # church 0, the leaf base for the count
|
|
71
|
+
|
|
72
|
+
NODE_COUNT: Builder = app(Y, lam(lambda self_recursion: lam(lambda quoted: ap(
|
|
73
|
+
quoted,
|
|
74
|
+
lam(lambda index: app(SUCC, _ZERO)), # QVar: one node
|
|
75
|
+
lam(lambda body: app(SUCC, app(self_recursion, body))), # QLam: one + body
|
|
76
|
+
lam(lambda function: lam(lambda argument: app(SUCC, ap(
|
|
77
|
+
PLUS, app(self_recursion, function), app(self_recursion, argument),
|
|
78
|
+
)))), # QApp: one + function + argument
|
|
79
|
+
))))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@curry
|
|
83
|
+
def node_count_at_most(bound: Builder, quoted: Builder) -> Builder:
|
|
84
|
+
"""``NODE_COUNT quoted <= bound`` (a Church boolean): the small-enough-to-de-table certificate."""
|
|
85
|
+
return ap(_AT_MOST, app(NODE_COUNT, quoted), bound)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@curry
|
|
89
|
+
def loose_bound_at_most(bound: Builder, quoted: Builder) -> Builder:
|
|
90
|
+
"""``LOOSE_BOUND quoted <= bound`` (a Church boolean): the few-free-variables certificate (an open
|
|
91
|
+
region with at most ``bound`` free de Bruijn variables, so its host island is an arity-``bound`` native)."""
|
|
92
|
+
return ap(_AT_MOST, app(LOOSE_BOUND, quoted), bound)
|