When creating publicly facing modules, sometimes I wish to create pseudo-readonly tables using __index
and __newindex
(that is, __index
points to an internally stored table, __newindex
throws an error Attempt to modify a readonly table
)
One major caveat of this approach appears if my table is functioning as a registry or other means of storage that should be iterable. Under normal Lua, I would override __pairs
and __ipairs
, or __len
, which would allow iteration to occur as if the table was there verbatim. This capability was removed in Luau due to different future plans for iteration (according to the page, see Compatibility - Luau).
Rather than proposing the idea to implement these three metamethods, I decided to go after the underlying reason for needing those in the first place: I need a way to create iterable readonly tables.
Most of the benefits of these come from enforcing standards in public facing libraries, and becomes something akin to IReadOnlyList<T> or IReadOnlyDictionary<TKey, TValue>. My most common use case right now is a registry for game object information (like items), and my goal is to prevent programmers from accidentally modifying a game object that should otherwise be immutable during runtime. A similar use case exists in my modules that represent “concurrent” (I use that term lightly) storage entities, where iteration over these should work off of a deep copy of the storage so that incoming modifications don’t affect iteration. It’s pretty standard for this deep copy to be immutable.