Summary
The Lua FFI in src/Data/Number/Format.lua references an unbound variable number in every string.format call, while the actual Number argument is bound under a different parameter name (n). All three native formatting functions (toPrecisionNative, toFixedNative, toExponentialNative) are therefore broken: at runtime number resolves to the global number (which is nil), so string.format receives nil as its value and errors instead of formatting the supplied number.
Offending code
src/Data/Number/Format.lua:
return {
toPrecisionNative = (function(d) return function(n) return string.format("%." .. tostring(d) .. "f", number) end end),
toFixedNative = (function(d) return function(n) return string.format("%." .. tostring(d) .. "d", number) end end),
toExponentialNative = (function(d) return function(n) return string.format("%." .. tostring(d) .. "e", number) end end),
toString = (function(num) return tostring(num) end)
}
The PureScript declarations (src/Data/Number/Format.purs) are curried as Int -> Number -> String:
foreign import toPrecisionNative :: Int -> Number -> String
foreign import toFixedNative :: Int -> Number -> String
foreign import toExponentialNative :: Int -> Number -> String
So the outer parameter d is the precision/digits Int, and the inner parameter n is the Number to be formatted. The format string correctly interpolates d, but the value passed to string.format should be n, not the non-existent number.
The reference JS FFI (src/Data/Number/Format.js) confirms the intended shape — outer arg is the digit count, inner arg is the number being formatted:
function wrap(method) {
return function(d) {
return function(num) {
return method.apply(num, [d]);
};
};
}
export const toPrecisionNative = wrap(Number.prototype.toPrecision);
export const toFixedNative = wrap(Number.prototype.toFixed);
export const toExponentialNative = wrap(Number.prototype.toExponential);
Root cause
Hand-written Lua FFI typo: the inner closure parameter is named n, but the body references number. Since number is never bound in any enclosing scope, Lua reads it as a global, which is nil. The n parameter is consequently never used. This is a pure copy/rename mistake in the Lua port — the function bodies were likely adapted from a version that named the parameter number and the rename to n was applied to the signature only, not the body.
Impact
Every toStringWith call (via precision, fixed, or exponential) routes through these functions, so number formatting is entirely non-functional in the Lua backend:
> string.format("%.6f", nil)
stdin:1: bad argument #2 to 'format' (number expected, got nil)
toString is unaffected (it correctly uses num).
How it was found
luacheck --quiet --std min src/ on the freshly cloned master:
src/Data/Number/Format.lua:2:52: unused argument n
src/Data/Number/Format.lua:2:104: accessing undefined variable number
src/Data/Number/Format.lua:3:48: unused argument n
src/Data/Number/Format.lua:3:100: accessing undefined variable number
src/Data/Number/Format.lua:4:54: unused argument n
src/Data/Number/Format.lua:4:106: accessing undefined variable number
Fix
Replace number with n in all three string.format calls so each function uses its actual bound argument.
Related (out of scope here)
toFixedNative uses the %d (integer) conversion specifier rather than %f. JavaScript's Number.prototype.toFixed(d) produces a fixed-point decimal with d fractional digits, so %d is semantically wrong for a floating-point value (and on Lua 5.3+ string.format("%.3d", 1.5) raises "number has no integer representation"). This is a separate correctness defect from the unbound-variable bug reported here; flagging it so it is not lost, but the fix in this issue is intentionally scoped to the undefined-variable / unused-argument defect only.
Summary
The Lua FFI in
src/Data/Number/Format.luareferences an unbound variablenumberin everystring.formatcall, while the actualNumberargument is bound under a different parameter name (n). All three native formatting functions (toPrecisionNative,toFixedNative,toExponentialNative) are therefore broken: at runtimenumberresolves to the globalnumber(which isnil), sostring.formatreceivesnilas its value and errors instead of formatting the supplied number.Offending code
src/Data/Number/Format.lua:The PureScript declarations (
src/Data/Number/Format.purs) are curried asInt -> Number -> String:So the outer parameter
dis the precision/digitsInt, and the inner parameternis theNumberto be formatted. The format string correctly interpolatesd, but the value passed tostring.formatshould ben, not the non-existentnumber.The reference JS FFI (
src/Data/Number/Format.js) confirms the intended shape — outer arg is the digit count, inner arg is the number being formatted:Root cause
Hand-written Lua FFI typo: the inner closure parameter is named
n, but the body referencesnumber. Sincenumberis never bound in any enclosing scope, Lua reads it as a global, which isnil. Thenparameter is consequently never used. This is a pure copy/rename mistake in the Lua port — the function bodies were likely adapted from a version that named the parameternumberand the rename tonwas applied to the signature only, not the body.Impact
Every
toStringWithcall (viaprecision,fixed, orexponential) routes through these functions, so number formatting is entirely non-functional in the Lua backend:toStringis unaffected (it correctly usesnum).How it was found
luacheck --quiet --std min src/on the freshly clonedmaster:Fix
Replace
numberwithnin all threestring.formatcalls so each function uses its actual bound argument.Related (out of scope here)
toFixedNativeuses the%d(integer) conversion specifier rather than%f. JavaScript'sNumber.prototype.toFixed(d)produces a fixed-point decimal withdfractional digits, so%dis semantically wrong for a floating-point value (and on Lua 5.3+string.format("%.3d", 1.5)raises "number has no integer representation"). This is a separate correctness defect from the unbound-variable bug reported here; flagging it so it is not lost, but the fix in this issue is intentionally scoped to the undefined-variable / unused-argument defect only.