Lua Mixins
A mixin is a table of shared methods that can be added to more than one class.
A mixin method can be copied into one class
Printable holds a printName method. Dog copies that method, so a dog object can call it.
local Printable = {}
function Printable:printName()
return "Name: " .. self.name
end
local Dog = {}
Dog.__index = Dog
function Dog.new(name)
local self = setmetatable({}, Dog)
self.name = name
return self
end
Dog.printName = Printable.printName
local d = Dog.new("Rex")
print(d:printName())The same mixin can serve two classes
Dog and Cat both copy printName from Printable. Each object prints its own name through the shared method.
local Printable = {}
function Printable:printName()
return "Name: " .. self.name
end
local Dog = {}
Dog.__index = Dog
function Dog.new(name)
local self = setmetatable({}, Dog)
self.name = name
return self
end
Dog.printName = Printable.printName
local Cat = {}
Cat.__index = Cat
function Cat.new(name)
local self = setmetatable({}, Cat)
self.name = name
return self
end
Cat.printName = Printable.printName
local d = Dog.new("Rex")
local c = Cat.new("Milo")
print(d:printName())
print(c:printName())One class can take methods from two mixins
Flyable holds fly, and Swimmable holds swim. Duck copies one method from each mixin, so one duck can do both.
local Flyable = {}
function Flyable:fly()
return self.name .. " flies"
end
local Swimmable = {}
function Swimmable:swim()
return self.name .. " swims"
end
local Duck = {}
Duck.__index = Duck
function Duck.new(name)
local self = setmetatable({}, Duck)
self.name = name
return self
end
Duck.fly = Flyable.fly
Duck.swim = Swimmable.swim
local d = Duck.new("Daisy")
print(d:fly())
print(d:swim())Example
local Greetable = {}
function Greetable:greet()
return "Hello from " .. self.name
end
local function applyMixin(class, mixin, names)
for _, name in ipairs(names) do
class[name] = mixin[name]
end
end
local Dog = {}
Dog.__index = Dog
function Dog.new(name)
local self = setmetatable({}, Dog)
self.name = name
return self
end
function Dog:speak()
return self.name .. " says Woof"
end
local Cat = {}
Cat.__index = Cat
function Cat.new(name)
local self = setmetatable({}, Cat)
self.name = name
return self
end
function Cat:speak()
return self.name .. " says Meow"
end
applyMixin(Dog, Greetable, {"greet"})
applyMixin(Cat, Greetable, {"greet"})
local d = Dog.new("Rex")
local c = Cat.new("Milo")
print(d:greet())
print(d:speak())
print(c:greet())
print(c:speak())Key points
- Shared table: A mixin table holds methods that do not belong to just one class.
- Copy into class: Copying a mixin method into a class table gives that class the method.
- More than one class: The same mixin can be copied into two different classes.
- Own methods stay: A class keeps its own methods as well as the copied mixin methods.
- Self still works: A copied method is a shared function value, but self still refers to the object that calls it.
In this example, Greetable holds a greet method, and applyMixin copies named methods from a mixin into a class. Dog and Cat each keep their own speak method and both gain greet. A dog and a cat are made, and each one greets and then speaks in its own way.
Note
A mixin needs no parent class, because its methods are copied in directly rather than found through a chain.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
How does a class gain a method from a mixin?