monome/norns

tab.readonly doesn't endow pairs/ipairs for proxy

Opened this issue · 0 comments

issue description + repro

going through documentation tasks, i realized that i expect tab.readonly to create a proxy which also inherits other tab functionality, eg. tab.print.

repro script

function init()
  
  mana = {
    white = {"order", "peace", "light"},
    blue = {"intellect", "logic", "manipulation"},
    black = {"power", "death", "corruption"},
    red = {"freedom", "chaos", "fury"},
    green = {"life", "nature", "evolution"}
  }
  
  mana_green_editable = tab.readonly{
    table = mana, -- which table to copy
    except = {'green'}, -- which keys should remain writeable
  }
  
  tab.print(mana_green_editable)
  
end

since tab.print calls pairs, running the above results in a C stack overflow pointing to

__pairs = function (_) return pairs(proxy) end,

workaround

i'm v dumb about metatable stuff, but i'm able to resolve the stack overflow issue by building and adjusting a local tpairs table which reflects expose + except params. see https://www.diffchecker.com/fNViUcZr/ for side-by-side to current code (chose a different name just for differentiation in testing):

pairs workaround
function treadonly(params)
  local t = params.table
  local exceptions = params.except or {}
  local proxy = {}
  local tpairs = {}
  for k,v in pairs(t) do
    if params.expose == nil or tab.contains(params.expose, k) then
      tpairs[k] = v
    end
  end
  local mt = {
    __index = function(_, k)
      if params.expose == nil or tab.contains(params.expose, k) then
        tpairs[k] = t[k]
        return t[k]
      end
      return nil
    end,
    __newindex = function (_,k,v)
      if (tab.contains(exceptions, k)) then
        t[k] = v
        tpairs[k] = v
      else
        error("'"..k.."', a read-only key, cannot be re-assigned.")
      end
    end,
    __pairs = function (_) return pairs(tpairs) end,
    __ipairs = function (_) return ipairs(tpairs) end,
  }
  setmetatable(proxy, mt)
  return proxy
end

if this is at all reasonable, i'd be happy to PR this fix -- or i'd be happy to PR a much better one with some quick guidance <3