|
| 1 | +### Added |
| 2 | + |
| 3 | +- Effect/ST actions of two or more arguments no longer allocate a closure |
| 4 | + per executed statement (#265). Such an action is already saturated at |
| 5 | + its real arity when the uncurrying worker/wrapper split measures it, so |
| 6 | + the split fires there and magic-do only afterwards rewrites the worker's |
| 7 | + body into the nullary thunk an Effect value is — leaving every fully |
| 8 | + applied statement site to allocate that thunk and immediately force it. |
| 9 | + The late uncurry run cannot repair this: it splits manifest lambda |
| 10 | + chains, and the thunk sits inside a worker that is already n-ary. The |
| 11 | + new `absorbEffectThunk` pass instead widens the worker in place, moving |
| 12 | + the thunk's parameter onto its parameter list, so the site is one n-ary |
| 13 | + call: |
| 14 | + |
| 15 | + ```lua |
| 16 | + local Golden_EffectWorkerThunk_Test_report_S_w = function(tag, n) |
| 17 | + return function() |
| 18 | + local _ = Effect_Console_log(tag)() |
| 19 | + local _ = Effect_Console_log(Data_Show_showIntImpl(n))() |
| 20 | + return Effect_Console_log("-")() |
| 21 | + end |
| 22 | + end |
| 23 | + local _ = Golden_EffectWorkerThunk_Test_report_S_w("a", 1)() |
| 24 | + ``` |
| 25 | + |
| 26 | + becomes |
| 27 | + |
| 28 | + ```lua |
| 29 | + local Golden_EffectWorkerThunk_Test_report_S_w = function(tag, n) |
| 30 | + local _ = Effect_Console_log(tag)() |
| 31 | + local _ = Effect_Console_log(Data_Show_showIntImpl(n))() |
| 32 | + return Effect_Console_log("-")() |
| 33 | + end |
| 34 | + local _ = Golden_EffectWorkerThunk_Test_report_S_w("a", 1) |
| 35 | + ``` |
| 36 | + |
| 37 | + The action's curried wrapper grows one parameter so a partial |
| 38 | + application still evaluates to a closure. Taking the run marker into |
| 39 | + the call also makes a recursive driver's self-call a genuine tail call, |
| 40 | + which the native-loop lowering then turns into a Lua `while` — the |
| 41 | + driver of `Bench.EffectStep` went from |
| 42 | + |
| 43 | + ```lua |
| 44 | + local Bench_EffectStep_go_S_w |
| 45 | + Bench_EffectStep_go_S_w = function(i, ref) |
| 46 | + return function() |
| 47 | + local _ = Bench_EffectStep_step_S_w(ref) |
| 48 | + if i >= 1 and i ~= 1 then |
| 49 | + return Bench_EffectStep_go_S_w(i - 1, ref)() |
| 50 | + else |
| 51 | + return Control_Monad_ST_Internal_read(ref)() |
| 52 | + end |
| 53 | + end |
| 54 | + end |
| 55 | + ``` |
| 56 | + |
| 57 | + to |
| 58 | + |
| 59 | + ```lua |
| 60 | + local Bench_EffectStep_go_S_w = function(i, ref) |
| 61 | + while true do |
| 62 | + local _ = Bench_EffectStep_step_S_w(ref) |
| 63 | + if i >= 1 and i ~= 1 then |
| 64 | + i, ref = i - 1, ref |
| 65 | + else |
| 66 | + return Control_Monad_ST_Internal_read(ref)() |
| 67 | + end |
| 68 | + end |
| 69 | + end |
| 70 | + ``` |
| 71 | + |
| 72 | + 1.22× faster under PUC Lua 5.1 and 1.28× under LuaJIT on the new |
| 73 | + `Bench.EffectStep2` macrobenchmark, and 1.10×/1.14× on the pre-existing |
| 74 | + `Bench.EffectStep`, whose two-argument driver is the same case; that |
| 75 | + spec's trace report loses one `NYI: bytecode FNEW` abort and its driver |
| 76 | + ends compiled as an `ILOOP` instead of interpreted as an `IFUNCF`. |
| 77 | + |
| 78 | + A worker whose call is bound as an action value and run later keeps the |
| 79 | + call-then-force shape, as does any other reference the widened arity |
| 80 | + would leave under-applied: the Lua backend drops a trailing unused |
| 81 | + parameter run, so an under-applied worker call would run the effect at |
| 82 | + construction time. `Golden.EffectWorkerThunk` pins both sides, including |
| 83 | + a `let`-bound local worker. |
0 commit comments