From 40c0f1e0634a9aae815f3c6ac7b802ba49559e1d Mon Sep 17 00:00:00 2001 From: Yura Lazarev Date: Mon, 13 Jul 2026 15:30:07 +0200 Subject: [PATCH] perf: back say and Http.Status.set with EffectFn1 foreigns The curried String -> Effect Unit FFI shape compiles every call into two Lua calls plus a fresh effect-thunk closure. Declaring the foreigns as EffectFn1 (sayImpl, setImpl) behind thin public wrappers keeps the API unchanged while letting the compiler collapse saturated call sites into one direct call: M.say("x")() becomes sayImpl("x"). Demonstrated by a differential link of a probe app against this branch vs main. Refs purescript-lua/purescript-lua#186. --- changelog.d/20260713_153000_effectfn_ffi.md | 7 +++++++ src/Lua/Ngx.lua | 2 +- src/Lua/Ngx.purs | 5 ++++- src/Lua/Ngx/Http/Status.lua | 2 +- src/Lua/Ngx/Http/Status.purs | 7 ++++++- 5 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 changelog.d/20260713_153000_effectfn_ffi.md diff --git a/changelog.d/20260713_153000_effectfn_ffi.md b/changelog.d/20260713_153000_effectfn_ffi.md new file mode 100644 index 0000000..a802837 --- /dev/null +++ b/changelog.d/20260713_153000_effectfn_ffi.md @@ -0,0 +1,7 @@ +### Changed + +- `say` and `Lua.Ngx.Http.Status.set` are backed by `EffectFn1` foreigns + (`sayImpl`, `setImpl`) behind thin public wrappers; the public API is + unchanged. A saturated `say s` / `set st` in compiled code is now a single + direct Lua call with no per-call thunk allocation (previously two calls + and a fresh closure per invocation). diff --git a/src/Lua/Ngx.lua b/src/Lua/Ngx.lua index 1c62c1d..88b9ae3 100644 --- a/src/Lua/Ngx.lua +++ b/src/Lua/Ngx.lua @@ -1 +1 @@ -return {say = (function(msg) return function() ngx.say(msg) end end)} +return {sayImpl = (function(msg) ngx.say(msg) end)} diff --git a/src/Lua/Ngx.purs b/src/Lua/Ngx.purs index 8cc8acc..d2804ba 100644 --- a/src/Lua/Ngx.purs +++ b/src/Lua/Ngx.purs @@ -2,6 +2,9 @@ module Lua.Ngx where import Data.Unit (Unit) import Effect (Effect) +import Effect.Uncurried (EffectFn1, runEffectFn1) -foreign import say :: String -> Effect Unit +foreign import sayImpl :: EffectFn1 String Unit +say :: String -> Effect Unit +say = runEffectFn1 sayImpl diff --git a/src/Lua/Ngx/Http/Status.lua b/src/Lua/Ngx/Http/Status.lua index c4f4164..4ddafff 100644 --- a/src/Lua/Ngx/Http/Status.lua +++ b/src/Lua/Ngx/Http/Status.lua @@ -36,5 +36,5 @@ return { versionNotSupported = (ngx.HTTP_VERSION_NOT_SUPPORTED), insufficientStorage = (ngx.HTTP_INSUFFICIENT_STORAGE), get = (function() return ngx.status end), - set = (function(status) return function() ngx.status = status end end) + setImpl = (function(status) ngx.status = status end) } diff --git a/src/Lua/Ngx/Http/Status.purs b/src/Lua/Ngx/Http/Status.purs index da349d7..a5fcb7e 100644 --- a/src/Lua/Ngx/Http/Status.purs +++ b/src/Lua/Ngx/Http/Status.purs @@ -2,6 +2,7 @@ module Lua.Ngx.Http.Status where import Data.Unit (Unit) import Effect (Effect) +import Effect.Uncurried (EffectFn1, runEffectFn1) foreign import data Status :: Type @@ -42,5 +43,9 @@ foreign import gatewayTimeout :: Status foreign import versionNotSupported :: Status foreign import insufficientStorage :: Status -foreign import set :: Status -> Effect Unit +foreign import setImpl :: EffectFn1 Status Unit + +set :: Status -> Effect Unit +set = runEffectFn1 setImpl + foreign import get :: Effect Status