This first edition was written for Lua 5.0. While still largely relevant for later versions, there are some differences.
The fourth edition targets Lua 5.3 and is available at Amazon and other bookstores.
By buying the book, you also help to support the Lua project.


13.3 – Library-Defined Metamethods

It is a common practice for some libraries to define their own fields in metatables. So far, all the metamethods we have seen are for the Lua core. It is the virtual machine that detects that the values involved in an operation have metatables and that these metatables define metamethods for that operation. However, because the metatable is a regular table, anyone can use it.

The tostring function provides a typical example. As we saw earlier, tostring represents tables in a rather simple format:

    print({})      --> table: 0x8062ac0
(Note that print always calls tostring to format its output.) However, when formatting an object, tostring first checks whether the object has a metatable with a __tostring field. If this is the case, tostring calls the corresponding value (which must be a function) to do its job, passing the object as an argument. Whatever this metamethod returns is the result of tostring.

In our example with sets, we have already defined a function to present a set as a string. So, we need only to set the __tostring field in the set metatable:

    Set.mt.__tostring = Set.tostring
After that, whenever we call print with a set as its argument, print calls tostring that calls Set.tostring:
    s1 = Set.new{10, 4, 5}
    print(s1)    --> {4, 5, 10}

The setmetatable/getmetatable functions use a metafield also, in this case to protect metatables. Suppose you want to protect your sets, so that users can neither see nor change their metatables. If you set a __metatable field in the metatable, getmetatable will return the value of this field, whereas setmetatable will raise an error:

    Set.mt.__metatable = "not your business"
    
    s1 = Set.new{}
    print(getmetatable(s1))     --> not your business
    setmetatable(s1, {})
      stdin:1: cannot change protected metatable