If you want live delegation (thats where changes in Super
automatically visible in Sub
+ no method copying), use a metatable __index
chain. This is standard in Luau.
If you’re going for just snapshot copies (frozen behavior, no lookup hops, manual conflict resolution + higher memory usage) then do mixin/merge, aka shallow copies.
Don’t do long dynamic chains during runtime though, do them once when defining your classes.
For multiple inheritance (whenever its needed), use a function-valued __index
that walks a list of supers, or you can pre-flatten into a method table. Don’t try to splice new metatables deep into an existing chain.
Here’s an example of an ‘extender’ function that uses chaining.
local function Extend(Super)
local Sub = {}
Sub.__index = Sub
setmetatable(Sub, {__index = Super})
return Sub
end
local Animal = {}
Animal.__index = Animal
function Animal.new(name : string)
local self = setmetatable({}, Animal)
self.Name = name
return self
end
function Animal:Speak()
print(self.Name .. " makes a noise")
end
local Dog = Extend(Animal)
function Dog:Speak()
print(self.Name .. " barks")
end
and then some other random part in your codebase…
local rover = Dog.new("Rover") -- still calls Animal.new because Dog doesn't override
rover:Speak() -- "Rover barks"
For your actual code in particular, you have some issues though.
Inherit(Sub, Super)
-
This walks chains at runtime then sets new metatables at l
ast.__index or last
which is fragile. If you have a metatable already that uses a function__index
, your structure is gonezo. -
Mutating existing class tables after definition is harder to reason from a dev perspective than building a hierarchy.
-
And lastly, if
Sub
already has an__index
pointing to itself (which is normal), chaining via thatlast.__index or last
may attach below the wrong node depending on what layout you use. Not a good general application function.
Mix(Table, Meta)
-
If the table already has a metatable, you’ll be overwriting keys in its metatable, not its class table, which… like, you never really wanna do that ever.
-
Merge(Meta, last)
copies meta keys into the existing meta, not into the class. -
This is small but you don’t need to use
pairs()
in Luau. It’s built into the syntax; the interpreter decides the best iterator to use when parsing.
Consider doing something more functional, like imperative inheritance abstraction:
local function ApplyMixin(Target, Mixin)
for k, v in Mixin do
if Target[k] == nil then
Target[k] = v
-- missing some error handling but yeah
end
end
end
local function Extend(Super)
-- Sub "class" table
local Sub = {}
Sub.__index = Sub
-- inheritance: Sub inherits methods from Super
setmetatable(Sub, {__index = Super})
return Sub
end
local Base = {}
Base.__index = Base
local Derived = Extend(Base)
ApplyMixin(Derived, Serializable) -- example method
ApplyMixin(Derived, Observable) -- other example method
And this is all noting that really inheritance is mostly useless in Roblox Luau anyway. It’s not really an OOP language. General functional programming with occasional OOP ideology will serve you best. It’s also far more readable and understandable. There’s a reason you’re getting little replies. Keeping it simple like this is what’ll work best in the long term.