Skip to content

Represent unit as an interned scalar instead of a table #15

Description

@Unisay

Problem

unit = {} (v7.2.0, chosen so Array Unit survives — Lua tables cannot hold nil) is a reference-typed value: it allocates and carries object identity. That is what forced the v7.3.1 @inline unit never pin: without it the compiler's inliner can paste the constructor into use sites, allocating a fresh {} per use and breaking Lua-== identity against the shared binding. And the singleton was never ecosystem-wide anyway: a foreign module cannot reference the prelude binding (FFI files are self-contained header + return table), so Unit-returning FFI invents its own value — and a forgotten return silently yields nil, re-collapsing Array Unit at the FFI boundary:

-- a Unit-returning FFI thunk with no explicit return:
log = function(s) return function() print(s) end end
-- the thunk returns nil; an Array Unit collected from it collapses to {}

Proposal

Represent unit as an interned scalar:

-- Data/Unit.lua
return { unit = "unit" }

Lua interns strings, so every occurrence of the literal is one object per VM and comparison is pointer equality — a natural singleton with zero per-use allocation, the Lua analogue of purerl's unit atom. Value semantics dissolves the identity question instead of solving it.

Flip the pragma to @inline unit always: a scalar is legitimately free to paste, so the binding and accessor dissolve entirely and every use site carries the literal. Update the FFI guidance: a Unit-returning foreign returns the literal "unit".

Alternatives considered

nil is forbidden (table storage). false is falsy — the if x then FFI idiom would silently lose unit values. 0 is equivalent in cost but worse in debug output (print of a unit says unit with the string).

Risks / Migration

FFI dispatching on type(x) == "table" over polymorphic values will see a string (worth a grep across the forks); serialization of records with Unit fields (e.g. cjson under OpenResty) changes from {} to "unit" — both behaviors were unspecified. Nothing can have relied on unit identity (see Problem: FFI-side units were never the shared table). Migration: prelude release → package-set bump → pslua eval goldens catch regressions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions