Skip to content

Instantly share code, notes, and snippets.

@aiya000
Created October 24, 2025 16:42
Show Gist options
  • Save aiya000/58ea5d3cc8b753a075b4a9600f8a9608 to your computer and use it in GitHub Desktop.
Save aiya000/58ea5d3cc8b753a075b4a9600f8a9608 to your computer and use it in GitHub Desktop.
How to create freezed table in Lua
--- readonly() {{{
---Creates a readonly table
---@generic K, V
---@param x table<K, V>
---@return table<K, V> --But readonly
local function readonly(x)
return setmetatable({}, {
__index = x,
__newindex = function(_, key, value)
error(('The table is readonly. { key = %s, value = %s }'):format(key, tostring(value)))
end,
__metatable = false
})
end
local function test_readonly()
-- Good case
local x = readonly({
a = 10,
})
local ok1, result1 = pcall(function()
x.a = 20
end)
-- false
vim.notify('readonly() - 1: Should be false => ' .. vim.inspect({ ok1, result1 }), vim.log.levels.INFO)
-- Bad case
local y = readonly({
a = {
b = 10,
},
})
local ok2, result2 = pcall(function()
y.a.b = 20
end)
-- true
vim.notify('readonly() - 2: Should be true => ' .. vim.inspect({ ok2, result2 }), vim.log.levels.INFO)
end
test_readonly()
-- }}}
--- deep_readonly() {{{
---Creates a deeply readonly table
---@generic K, V
---@param x table<K, V>
---@return table<K, V> --But deeply readonly
local function deep_readonly(x)
---@generic K, V
---@param a table<K, V>
---@return table<K, V>
local function create_deep_readonly(a)
return setmetatable({}, {
__index = deep_readonly(a),
__newindex = function(_, key, value)
error(('The table is readonly. { key = %s, value = %s }'):format(key, tostring(value)))
end,
__metatable = false
})
end
local result = {}
for key, value in pairs(x) do
if type(value) == 'table' then
result[key] = create_deep_readonly(value)
else
result[key] = value
end
end
return readonly(result)
end
local function test_deep_readonly()
-- Good case
local x = deep_readonly({
a = 10,
})
local ok1, result1 = pcall(function()
x.a = 20
end)
-- false
vim.notify('deep_readonly() - 1: Should be false => ' .. vim.inspect({ ok1, result1 }), vim.log.levels.INFO)
-- Also, Good case
local y = deep_readonly({
a = {
b = 10,
},
})
local ok2, result2 = pcall(function()
y.a.b = 20
end)
-- false
vim.notify('deep_readonly() - 2: Should be false => ' .. vim.inspect({ ok2, result2 }), vim.log.levels.INFO)
end
test_deep_readonly()
-- }}}
-- NOTE: This is useless... [[
-- readonly_value() {{{
---Simular to `readonly()`
---```lua
----- These are same:
---local x = readonly({ value = 10 })
---local y = readonly_value(10)
---```
---@generic T
---@param value T
---@return { value: T }
local function readonly_value(value)
return readonly({
value = value,
})
end
local function test_readonly_value()
local x = readonly_value(10)
local ok1, result1 = pcall(function()
x = 20
end)
vim.notify('readonly_value(): Should be false => ' .. vim.inspect({ ok1, result1 }), vim.log.levels.INFO)
end
-- Commented out because this one does fails as a matter of course.
-- test_readonly_value()
-- }}}
-- ]]
@aiya000
Copy link
Author

aiya000 commented Oct 24, 2025

readonly_value()はconst変数を作れるようにしようと思って、途中で生み出した成果(成果ではないかも)
もしかしたら以下で(全てがconstになることを無視すれば)モジュールconst変数ができるかも

local M = {}

-- M.fooなどを定義

return readonly(M) -- `return M`の代わりにモジュールごと

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment