feat: represent application as an n-ary AppN call node (#179) - #199
Merged
Conversation
Replace the IR's unary `App ann f a` with `AppN ann f (args :| ...)`, one Lua call passing every argument. `App` stays as a bidirectional pattern synonym for the singleton, so existing unary rewrite rules compile unchanged and currying keeps being expressed by nesting. - Total case analyses (getAnn, setAnn, subexpressions, alphaEq) move to AppN; alphaEq compares arity and pairs arguments positionally. - The Lua backend emits a single n-ary call for a multi-argument AppN, preserving the lone Prim.undefined elision for the nullary case. - A new linter invariant (WellApplied) rejects a literal lambda applied to more than one argument in one call, since a lambda is a one-parameter Lua function. Representational groundwork for lifting the *.Uncurried wrappers to direct calls. On its own it leaves generated code unchanged: golden.lua and eval goldens are untouched; golden.ir churns mechanically with the AppN Show shape.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the compiler IR to represent function application as an n-ary call node (AppN) while preserving the existing unary App API via a bidirectional pattern synonym. This lays the groundwork for future optimization work that can collapse uncurried wrapper spines into single Lua calls.
Changes:
- Replaced the unary IR constructor
AppwithAppN ann f (NonEmpty args)and reintroducedAppas a bidirectional pattern synonym for the singleton case. - Updated core traversals/comparisons (
getAnn,setAnn,subexpressions,alphaEq) and Lua codegen to support emitting a single n-ary Lua call for multi-argumentAppN. - Added a new IR linter invariant (
WellApplied) plus unit tests to prevent miscompiling multi-argument calls where the callee is a literal lambda.
Reviewed changes
Copilot reviewed 39 out of 52 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| lib/Language/PureScript/Backend/IR/Types.hs | Introduces AppN, documents semantics, adds App pattern synonym, updates traversals and alphaEq. |
| lib/Language/PureScript/Backend/Lua.hs | Emits n-ary Lua calls from AppN (including Prim.undefined elision for nullary calls). |
| lib/Language/PureScript/Backend/IR/Linter.hs | Adds WellApplied invariant and detection of over-applied literal lambdas under AppN. |
| lib/Language/PureScript/Backend/IR/Pass.hs | Wires WellApplied into the checked pass runner as a new invariant option. |
| lib/Language/PureScript/Backend/IR/Optimizer.hs | Updates imports to use the App pattern synonym where needed. |
| lib/Language/PureScript/Backend/IR/MagicDo.hs | Updates imports to use the App pattern synonym where needed. |
| lib/Language/PureScript/Backend/IR/FlattenDeepBinds.hs | Updates imports to use the App pattern synonym where needed. |
| test/Language/PureScript/Backend/Lua/Spec.hs | Adds coverage asserting AppN with multiple args lowers to a single Lua call. |
| test/Language/PureScript/Backend/IR/Types/Spec.hs | Adds alphaEq tests covering AppN arity sensitivity and matching. |
| test/Language/PureScript/Backend/IR/Linter/Spec.hs | Adds tests for WellApplied (including descent into nested call arguments). |
| test/Language/PureScript/Backend/IR/FlattenDeepBinds/Spec.hs | Updates imports to use the App pattern synonym. |
| changelog.d/20260707_160000_unisay_nary_appn.md | Changelog entry describing the IR change and new invariant. |
| test/ps/output/Golden.Unbinding.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.TailRecM2Shadow.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.StringEscapes.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.RecursiveBindings.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.RecGroupOrder.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.RecDataDefs.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.ProfunctorDictLens.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.PatternMatching.Test2/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.Nested.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.NameShadowing.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.MaybeChainModule.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.MaybeChain.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.Issue37.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.Inline.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.HelloPrelude.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.ForeignSharing.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.FloatIn.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.Fibonacci.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.DerivedFunctor.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.Currying.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.CharLiterals.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.CaseStatements.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.BugListGenericEq.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.ArrayPatternMatch.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.ArrayOfUnits.Test/golden.ir | Golden IR updated for AppN shape. |
| test/ps/output/Golden.Annotations.M2/golden.ir | Golden IR updated for AppN shape. |
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.
Implements the representational half of #179: the IR gains an n-ary application node, the groundwork for lifting the uncurried
*.Uncurriedwrappers to direct calls.What changed
App ann f abecomes the singleton case ofAppN ann f (args), one Lua call passing every argument.Appstays as a bidirectional pattern synonym, so every unary rewrite rule (beta, magicDo, flattenDeepBinds) compiles unchanged. Currying stays expressed by nesting:AppN f [a, b](one callf(a, b)) andAppN (AppN f [a]) [b](two callsf(a)(b)) are different programs, because Lua drops surplus arguments and nils missing ones.AppN:getAnn,setAnn,subexpressions(visits every argument), andalphaEq(compares arity, then pairs arguments positionally).AppN, keeping the lonePrim.undefinedelision for the nullary case.WellApplied, rejects applying a literal lambda to more than one argument in one call. A lambda compiles to a one-parameter Lua function, so a well-formed multi-argumentAppNalways has a non-lambda head (a reference to an n-ary foreign). The check is wired into the pass pipeline, dormant until a pass introduces multi-argument nodes.Scope
This is the node only. Nothing produces a multi-argument
AppNyet, so generated code is unchanged. Lifting the*.Uncurriedwrappers (the perf-bearing half of the original #179) needs the source-derived lifter from #178 and is tracked in #198.Closes #179.
Verification
cabal test all: 557 examples, 0 failures, including 16 new tests (n-ary codegen, theWellAppliedinvariant, alphaEq arity sensitivity).golden.irchurns mechanically with theAppNShow shape.golden.luaandeval/golden.txtare untouched: the emitted Lua and its runtime output stay byte-identical.