🎓 A1Learner
🔍
0 of 52 lessons completed

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?