test(LambdaCalculus): port the lambda-n-ways normalization corpus - #791
Open
korbonits wants to merge 4 commits into
Open
test(LambdaCalculus): port the lambda-n-ways normalization corpus#791korbonits wants to merge 4 commits into
korbonits wants to merge 4 commits into
Conversation
Concrete tests for opening, closing, and substitution on locally nameless untyped lambda terms, in the spirit of lambda-n-ways: definition-pinning unit tests, capture scenarios, open/close/subst round-trips, and normalization of Church arithmetic through a fuel-bounded normalizer. Closes leanprover#711. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vendors every `X.lam`/`X.nf.lam` pair from the `lams/` directory of sweirich/lambda-n-ways and runs them against the locally nameless untyped λ-calculus: a parser for the `.lam` concrete syntax, a translation into `Term ℕ`, and a fuel-bounded normal-order normalizer transcribed from that project's `lib/LocallyNameless/Ott.hs`, built on `Term.open'`, `Term.close` and `Term.subst`. Each corpus is run twice, contracting β-redexes with `Term.open'` directly and by opening with a fresh variable and then substituting, so that every β-step also exercises `Term.subst`. The two agree by `subst_intro`. Trims the earlier hand-written normalizer and Church numeral tests, which this supersedes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The lambda-n-ways port supersedes them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #711. Supersedes #737, which I could not reopen —
gh pr reopenand the RESTPATCH state=openboth fail on it, and GitHub still reports that PR's head at the pre-rewrite commit, so it appears detached from the branch. Same branch, so the review history there is still readable; there's a summary comment at #737 (comment 5244547139).@chenson2018 — thanks for the clear steer on #737. This replaces the hand-written unit tests with an actual port of the
lams/corpus from sweirich/lambda-n-ways.What's here
Under
CslibTests/LambdaNWays/:lams/— all 36X.lam/X.nf.lampairs from upstream, vendored verbatim (1,492 terms), with a README recording provenance and the MIT notice. The only.lamfiles left out are the four with no recorded normal form (constructed,fact5,lennartchurch,simple) and the.eval.lamfiles, which record weak head normal forms for upstream's evaluation benchmark rather than normal forms.Basic.lean— a tokenizer/parser for the.lamconcrete syntax (including theletsugar thatlennart.lamneeds), the translation from named terms intoTerm ℕ, and a fuel-bounded normal-order normalizer transcribed from upstream'slib/LocallyNameless/Ott.hs, built onTerm.open',Term.closeandTerm.subst.Corpus.lean— one check per file, grouped the way upstream'stests/Main.hsgroups them: strictness, terms that have caught bugs, constructed terms, increasing substitution counts, random terms, and the single largelennartterm. The data is pulled in withinclude_str, so there is no file IO at test time.Two deliberate departures from a straight transcription
open/close, soTerm.substwould go untested — which seemed wrong for an issue about capture-avoiding substitution. So each corpus is checked once contracting a redex asTerm.open' b a, and once as(b ^ fvar v)[v := a]for a globally freshv. The two agree bysubst_intro, and the second routes all ~120k oflennart's substitutions throughTerm.subst.=. Upstream, working with a named representation, has to convert to de Bruijn form first.Verification
Rather than just trusting a green run, I mutation-tested the harness against deliberately broken primitives:
openRecthat doesn't shift under a binderrandom15, 9/9capture10closeRecthat doesn't shift under a binderrandom15, 9/9capture10substthat doesn't descend into abstractionsrandom15, 9/9capture10A mismatch reports the offending term and fails elaboration:
lake build --wfail --iofail,lake test --wfail --iofail,lake exe mk_all --checkandlake exe checkInitImportsall pass locally.Costs, in case you want them lower
Corpus.leantakes ~28s to elaborate and the data is 1.7MB. Dropping the second (Term.subst) pass halves the time; droppingrandom16–random19, which are near-duplicates ofrandom15/random20and are not in upstream'stests/Main.hs, would cut ~440KB. Happy to trim either — just say which.I also deleted
CslibTests/LambdaCalculusLocallyNameless.leanfrom the earlier version of this branch, since the port supersedes it.Use of AI
Per CONTRIBUTING: written with Claude Code (Opus 5), driven interactively by me. It read the upstream Haskell (
Util/Syntax/Lambda.hsfor the grammar,Util/Impl.hsfor the file format,LocallyNameless/Ott.hsfor the normalizer,tests/Main.hsfor the suite structure) and wrote the parser, normalizer and harness; I directed the scope, the decision to also exerciseTerm.subst, and the mutation testing above. The vendored.lamfiles are byte-for-byte copies of upstream and were not generated. Worth reviewing with fresh eyes: the parser's handling ofletand of application associativity, and the free-variable numbering inNamed.toLN(every corpus term happens to be closed, so that path is not exercised by the data).🤖 Generated with Claude Code