I’ve been working on a project that implements strict classes that throw errors if attempting to index or write to invalid fields; however, I realized that I’m not sure of a good way to implement properties that can be set to nil
, since setting the field to nil
will cause future indexing/writing to throw errors.
I’ve come up with a relatively simple implementation to solve the problem that can be seen below, but it has some maintainability concerns.
------------------------------------------------------------------------------------------
--//CLASS DEFINITION//--------------------------------------------------------------------
------------------------------------------------------------------------------------------
local class = {}
local class_nullable_fields = table.freeze({
["parent"] = true
})
class.__index = function(self, key)
if class_nullable_fields[key] then
return nil
else
return class[key]
end
end
class.__newindex = function(self, key, value)
if class_nullable_fields[key] then
rawset(self, key, value)
elseif class[key] then
error("Class property \"" .. tostring(key) .. "\" cannot be written to.")
end
end
--Define rest of class here...
setmetatable(class, {
__index = function(self, key)
error("\"" .. tostring(key) .. "\" is not a valid property of Class.")
end
})
table.freeze(class)
------------------------------------------------------------------------------------------
--//SUB-CLASS DEFINITION//----------------------------------------------------------------
------------------------------------------------------------------------------------------
local sub_class = {}
local sub_class_nullable_fields = table.freeze(setmetatable({
["position"] = true
}, {__index = class_nullable_fields}))
sub_class.__index = function(self, key)
if sub_class_nullable_fields[key] then
return nil
else
return sub_class[key]
end
end
sub_class.__newindex = function(self, key, value)
if sub_class_nullable_fields[key] then
rawset(self, key, value)
elseif sub_class[key] then
error("Subclass property \"" .. tostring(key) .. "\" cannot be written to.")
end
end
--Define rest of class here...
setmetatable(sub_class, class)
table.freeze(sub_class)
The primary maintainability concern is that inheritance either requires access to the table containing the nullable fields from superclasses, or it requires some other means of invoking the __newindex
metamethod of the super classes and only throwing an error if the write is legitimately illegal.
The other concern is performance. The Luau site strongly recommends using direct table references for __index
values, which I can’t do in this structure. This all being said, though, I am not at a point in the project where I can meaningfully ascertain the true performance impact of this, so this may be a moot point.
The problem can be side-stepped by assigning nullable values to false
instead of nil
, but this is both a design-limiter and a correctness nightmare. Is there an elegant solution to this problem that I’m missing, or is this just a situation where I’m going to have to make a trade-off and decide what I value most?