Last active
June 9, 2025 18:37
-
-
Save 44100hertz/c2b49452b198b17d2dd34799200957ea to your computer and use it in GitHub Desktop.
Immutable table library for Lua5.1 / LuaJIT
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--------------- pairs/ipairs polyfill | |
-- modified from: | |
-- https://gist.github.com/creationix/b56de2dde4021673f24242258efdcc80 | |
do | |
local function mm_exists(name, trig) | |
local triggered = false | |
trig(setmetatable({}, {[name] = function () triggered = true end})) | |
return triggered | |
end | |
if not mm_exists("__pairs", pairs) then | |
local next = pairs({}) | |
function _G.pairs(t) | |
local mt = getmetatable(t) | |
if mt and mt.__pairs then | |
return mt.__pairs(t) | |
else | |
return next, t, nil | |
end | |
end | |
end | |
if not mm_exists("__ipairs", ipairs) then | |
local iter = ipairs({}) | |
function _G.ipairs(t) | |
local mt = getmetatable(t) | |
if mt and mt.__ipairs then | |
return mt.__ipairs(t) | |
else | |
return iter, t, 0 | |
end | |
end | |
end | |
end | |
------------ immutable library | |
local origTable_symbol = {} | |
local immutable = {} | |
local next = pairs({}) | |
local function immutNext(t, k) | |
local kn, v = next(rawget(t, origTable_symbol), k) | |
return kn, immutable.freeze(v) | |
end | |
local iter = ipairs({}) | |
local function immutIter(t, k) | |
local kn, v = iter(rawget(t, origTable_symbol), k) | |
return kn, immutable.freeze(v) | |
end | |
local proxy_mt = { | |
__index = function (t, k) | |
local v = rawget(t, origTable_symbol)[k] | |
return immutable.freeze(v) | |
end, | |
__newindex = function (_, k, v) | |
error("Attempt to set field " .. tostring(k) .. " to value " .. tostring(v)) | |
end, | |
__pairs = function (immut) | |
return immutNext, immut, nil | |
end, | |
__ipairs = function (immut) | |
return immutIter, immut, 0 | |
end, | |
} | |
function immutable.freeze(t) | |
if type(t) == "table" then | |
return setmetatable({[origTable_symbol] = t}, proxy_mt) | |
end | |
return t | |
end | |
---@param t table | |
---@return table | |
function immutable.copyToMutable(t, seen) | |
seen = seen or {} | |
local copy = {} | |
local orig = rawget(t, origTable_symbol) or t | |
for k,v in pairs(orig) do | |
if type(v) == "table" then | |
if seen[v] then | |
-- TODO: can probably fix this... | |
error("Cyclical table, cannot trivially copy") | |
end | |
copy[k] = immutable.copyToMutable(v, seen) | |
seen[v] = true | |
else | |
copy[k] = v | |
end | |
end | |
return copy | |
end | |
---@param t table | |
---@param fn fun(t: table, ...): table | |
---@return table | |
function immutable.mutateWith(t, fn, ...) | |
local mut = immutable.copyToMutable(t) | |
return immutable.freeze(fn(mut, ...)) | |
end | |
---@param im table | |
---@param ass table | |
function immutable.assign(im, ass) | |
local mut = {} | |
-- no deep copy needed, so this is pretty fast :) | |
for k,v in pairs(im) do mut[k] = v end | |
for k,v in pairs(ass) do mut[k] = v end | |
return immutable.freeze(mut) | |
end | |
return immutable |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment