Search code examples
lualua-table

Where to define table member functions in Lua


As I understand it, there are multiple ways to define a member function to a table in Lua. For example, the following two appear to be equivalent:

-- method a)
local table1 = {x = 1, y = 2}
function table1:myfunc()
    return self.x + self.y
end

-- method b)
local table2 = {
    x = 1,
    y = 2,

    myfunc = function(self)
        return self.x + self.y
    end,
}

Having previously used Python, my instinct would be to use method b) to keep things more neatly grouped together. However reading through examples it seems that people generally use method a) by convention. I can't find any objective reason for why it should be superior though.

Indeed, it appears that on the contrary there are reasons to at least forward-declare the function variable when you initialise the table, like so:

local table3 = {x = 1, y = 2, myfunc}
function table3:myfunc()
    return self.x + self.y
end

This way Lua knows about the existence of the member from the beginning and can set up the hashes correctly, whereas increasing the number of members to an existing table may require a rehash (although I can't imagine this ever actually becoming a noticable performance problem unless maybe you do this for a huge number of small tables). For sources compare: https://www.lua.org/gems/sample.pdf

So is there any reason for not defining member functions directly during the definition of the table itself? Or is it just the fact that some syntactic sugar (the function name() syntax and the colon) is not available then?


Solution

  • As a functional programming nerd, I would prefer method b, because it defines the table all at once instead of mutating it once for every method we need to add. One thing you should be aware of is that some methods may call each other, and it's faster to use locals than to call self:myfunc().

    local function myfunc(self)
      return self.x + self.y
    end
    
    local myTable = {
      x = 1,
      y = 2,
      myfunc = myfunc,
    
      moreMyfunc = function(self)
        return myfunc(self) * 2
      end,
    }
    

    If the lack of symmetry between methods is bothersome, you can make all methods local and add them in the table constructor, like I just did with myfunc.

    Egor brought up another important point (which is more relevant if the table is a class):

    Method a allows you to access variable table1 as upvalue from inside your functions. Method b doesn't allow this.

    In this case, I would forward declare the table name. This has the advantage of changing a variable once, instead of mutating the table many times.

    local myTable
    
    local function myfunc(self)
      return self.x + self.y
    end
    
    local function moreMyfunc(self)
      return myfunc(self) * 2
    end
    
    myTable = {
      x = 1,
      y = 2,
      myfunc = myfunc,
      moreMyfunc = moreMyfunc,
    }