From 83962d766343954e6f65f76ef3a1c9ffdf8746f2 Mon Sep 17 00:00:00 2001 From: Yura Lazarev Date: Tue, 14 Jul 2026 09:01:37 +0200 Subject: [PATCH] feat: add errorWithName (upstream v6.1.0 parity) Upstream purescript-exceptions v6.1.0 exports errorWithName :: String -> String -> Error (message first, name second) and the fork did not, so code written against the registry package failed to compile against the fork. The fork models Error as a plain Lua string, so the name is unobservable through this binding: message returns the supplied message and name keeps answering the constant "Error", the same precedent as errorWithCause's dropped cause (documented in the FFI). An observable-name upgrade of the whole Error representation would be a separate issue. The inherited upstream Exception.js gains the same export so it keeps matching its upstream counterpart. Extends the regression guard to pin message/name behaviour, and ignores the dist/ build artifact. Closes purescript-lua/purescript-lua#269 --- .gitignore | 1 + changelog.d/20260714_090000_yura_errorwithname.md | 6 ++++++ src/Effect/Exception.js | 8 ++++++++ src/Effect/Exception.lua | 7 +++++++ src/Effect/Exception.purs | 4 ++++ test/regression/exception.lua | 15 ++++++++++++++- 6 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 changelog.d/20260714_090000_yura_errorwithname.md diff --git a/.gitignore b/.gitignore index b618ba9..d5bcf30 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ !/.lua-format !/.githooks/ /output/ +/dist/ diff --git a/changelog.d/20260714_090000_yura_errorwithname.md b/changelog.d/20260714_090000_yura_errorwithname.md new file mode 100644 index 0000000..0913bb7 --- /dev/null +++ b/changelog.d/20260714_090000_yura_errorwithname.md @@ -0,0 +1,6 @@ +### Added + +- `Effect.Exception.errorWithName` (upstream v6.1.0 parity, #269). Following + the fork's string-`Error` model, the name is unobservable: `message` + returns the supplied message and `name` keeps answering the constant + `"Error"`, the same precedent as `errorWithCause`'s dropped cause. diff --git a/src/Effect/Exception.js b/src/Effect/Exception.js index 7a55947..5e7b9d9 100644 --- a/src/Effect/Exception.js +++ b/src/Effect/Exception.js @@ -12,6 +12,14 @@ export function errorWithCause(msg) { }; } +export function errorWithName(msg) { + return function(name) { + const e = new Error(msg); + e.name = name; + return e; + }; +} + export function message(e) { return e.message; } diff --git a/src/Effect/Exception.lua b/src/Effect/Exception.lua index b5b47a2..63445dd 100644 --- a/src/Effect/Exception.lua +++ b/src/Effect/Exception.lua @@ -8,6 +8,13 @@ return { return msg end end), + errorWithName = (function(msg) + return function(_name) + -- The name is unobservable through this binding (`name` always answers + -- the constant "Error"), so it is dropped like errorWithCause's cause. + return msg + end + end), message = (function(err) return err end), name = (function(_err) -- JS `e.name || "Error"`; the string-Error model carries no name field. diff --git a/src/Effect/Exception.purs b/src/Effect/Exception.purs index 1e8e82b..705f198 100644 --- a/src/Effect/Exception.purs +++ b/src/Effect/Exception.purs @@ -6,6 +6,7 @@ module Effect.Exception , catchException , error , errorWithCause + , errorWithName , message , name , stack @@ -35,6 +36,9 @@ foreign import error :: String -> Error -- | Create a JavaScript error, specifying a message and a cause foreign import errorWithCause :: String -> Error -> Error +-- | Create a JavaScript error, specifying a message and a name +foreign import errorWithName :: String -> String -> Error + -- | Get the error message from a JavaScript error foreign import message :: Error -> String diff --git a/test/regression/exception.lua b/test/regression/exception.lua index 492c3b3..9da13b8 100644 --- a/test/regression/exception.lua +++ b/test/regression/exception.lua @@ -1,6 +1,6 @@ -- Regression guard for the Lua 5.1 FFI of Effect.Exception. -- --- Pins the four semantic fixes: +-- Pins the semantic fixes: -- #81 catchException must RUN the handler Effect (trailing `()`), not return -- the unexecuted thunk. -- #82 throwException must preserve the message across throw/catch; Lua's @@ -11,6 +11,10 @@ -- #84 errorWithCause keeps the message pristine (the cause is unobservable -- through this binding — there is no `cause` accessor — so it is dropped -- rather than concatenated into the message). +-- #269 errorWithName was missing from the fork. Upstream takes the message +-- first and the name second (errorWithName(msg)(name)); the name is +-- dropped like errorWithCause's cause, so `name` keeps answering the +-- documented constant "Error". -- -- `Effect a` in this backend is a zero-arg thunk that must be invoked to run. -- Run from the repo root: `lua test/regression/exception.lua`. @@ -87,6 +91,15 @@ end -- Sanity: message of a plain error is the message. do check("message of a plain error", E.message(E.error("boom")) == "boom", "got " .. tostring(E.message(E.error("boom")))) end +-------------------------------------------------------------------------------- +-- #269 errorWithName keeps the message; the name stays unobservable ---------- + +do + local e = E.errorWithName("boom")("TypeError") + check("errorWithName message is the supplied msg", E.message(e) == "boom", "got " .. tostring(E.message(e))) + check("errorWithName name stays \"Error\"", E.name(e) == "Error", "got " .. tostring(E.name(e))) +end + -------------------------------------------------------------------------------- if failures > 0 then error(failures .. " regression check(s) failed") end