Metamethods

Lua supports metamethods to provide some advanced features such as operator overloading. Like Lua tables, records support metamethods. To use metamethods in records you need to do two things:

  • declare the metamethods in the record type using the metamethod word to benefit from static type checking;
  • and assign the metatable with setmetatable as you would normally do in Lua to get the dynamic metatable behavior.

Here is a complete example, showing the metamethod declarations in the record block and the setmetatable declarations attaching the metatable.

local type Rec = record
   x: number
   metamethod __call: function(Rec, string, number): string
   metamethod __add: function(Rec, Rec): Rec
end

local rec_mt: metatable<Rec>
rec_mt = {
   __call = function(self: Rec, s: string, n: number): string
      return tostring(self.x * n) .. s
   end,
   __add = function(a: Rec, b: Rec): Rec
      local res: Rec = setmetatable({}, rec_mt)
      res.x = a.x + b.x
      return res
   end,
}

local r: Rec = setmetatable({ x = 10 }, rec_mt)
local s: Rec = setmetatable({ x = 20 }, rec_mt)

r.x = 12
print(r("!!!", 1000)) -- prints 12000!!!
print((r + s).x)      -- prints 32

Note that we explicitly declare variables as Rec when initializing the declaration with setmetatable. The Teal standard library definiton of setmetatable is function<T>(T, metatable<T>): T, so declaring the correct record type in the declaration assigns the record type to the type variable T in the return value of the function call, causing it to propagate to the argument types, matching the correct table and metatable types.

Operator metamethods for integer division // and bitwise operators are supported even when Teal runs on top of Lua versions that do not support them natively, such as Lua 5.1.