Status
Scoped to the n-ary AppN node (Option 1 of the original issue). Delivered in #199. The *.Uncurried lifting half, which carries the performance win and depends on the #178 lifter, moved to #198.
Problem
pslua has no way to express a single Lua call that passes several arguments. Every application is unary, so a saturated uncurried FFI call such as runSTFn2(pushImpl)(x)(arr)() compiles to a curried onion (two closure allocations plus a thunk per call), and the optimizer cannot collapse it because the IR cannot represent pushImpl(x, arr). The node added here is the representation the collapse needs; the collapse itself is #198.
Approach
AppN replaces App. The IR's unary App ann f a becomes AppN ann f (NonEmpty args), where the argument list is one Lua call with those arguments. The unary case is the singleton, and a bidirectional pattern synonym pattern App ann f a = AppN ann f (a :| []) keeps every existing unary rule (betaReduce, magicDo matching, flattenDeepBinds) compiling unchanged; multi-argument cases are added only where meaningful.
The list is not a flattened spine: AppN f [a, b] (one call f(a, b)) and AppN (AppN f [a]) [b] (two calls f(a)(b)) are different programs, because Lua drops surplus arguments and fills missing ones with nil. Currying stays expressed by nesting. A lint invariant (WellApplied) forbids applying a literal lambda to more arguments than it binds; since a lambda compiles to a one-parameter Lua function, a well-formed multi-argument AppN always has a non-lambda head.
n-ary abstraction (AbsN) is deliberately out of scope: n-ary callees already exist (FFI functions are n-ary in Lua source), while n-ary definitions only arise with full uncurrying (#24). This makes AppN the first installment of #24's infrastructure, with the arity given by types instead of analysis. For the same reason mkFnN (whose body is an n-ary function literal) defers to #24.
Prerequisites / Relations
The node depends only on #173 (done). It is the first installment of #24's infrastructure and, by giving #24 its n-ary call node, in turn unblocks #181. Lifting the *.Uncurried wrappers to direct AppN calls is #198, which additionally needs the #178 lifter.
Verification
On its own the node changes no generated code: golden.lua and the eval goldens stay byte-identical, while golden.ir churns mechanically with the AppN Show shape. New tests cover the n-ary codegen path, the WellApplied invariant, and alphaEq arity sensitivity. The performance measurement lives with the lifting work in #198.
Status
Scoped to the n-ary
AppNnode (Option 1 of the original issue). Delivered in #199. The*.Uncurriedlifting half, which carries the performance win and depends on the #178 lifter, moved to #198.Problem
psluahas no way to express a single Lua call that passes several arguments. Every application is unary, so a saturated uncurried FFI call such asrunSTFn2(pushImpl)(x)(arr)()compiles to a curried onion (two closure allocations plus a thunk per call), and the optimizer cannot collapse it because the IR cannot representpushImpl(x, arr). The node added here is the representation the collapse needs; the collapse itself is #198.Approach
AppN replaces App. The IR's unary
App ann f abecomesAppN ann f (NonEmpty args), where the argument list is one Lua call with those arguments. The unary case is the singleton, and a bidirectional pattern synonympattern App ann f a = AppN ann f (a :| [])keeps every existing unary rule (betaReduce, magicDo matching, flattenDeepBinds) compiling unchanged; multi-argument cases are added only where meaningful.The list is not a flattened spine:
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 fills missing ones with nil. Currying stays expressed by nesting. A lint invariant (WellApplied) forbids applying a literal lambda to more arguments than it binds; since a lambda compiles to a one-parameter Lua function, a well-formed multi-argumentAppNalways has a non-lambda head.n-ary abstraction (
AbsN) is deliberately out of scope: n-ary callees already exist (FFI functions are n-ary in Lua source), while n-ary definitions only arise with full uncurrying (#24). This makesAppNthe first installment of #24's infrastructure, with the arity given by types instead of analysis. For the same reasonmkFnN(whose body is an n-ary function literal) defers to #24.Prerequisites / Relations
The node depends only on #173 (done). It is the first installment of #24's infrastructure and, by giving #24 its n-ary call node, in turn unblocks #181. Lifting the
*.Uncurriedwrappers to directAppNcalls is #198, which additionally needs the #178 lifter.Verification
On its own the node changes no generated code:
golden.luaand the eval goldens stay byte-identical, whilegolden.irchurns mechanically with theAppNShow shape. New tests cover the n-ary codegen path, theWellAppliedinvariant, and alphaEq arity sensitivity. The performance measurement lives with the lifting work in #198.