4

I'm studying Lua, using the book Programming in Lua, first edition. I'm having trouble understanding metatables.

This is the code and explanations that appear on page 108:

Set = {}

function Set.new (t)
  local set = {}
  for _, l in ipairs(t) do set[l] = true end
  return set
end

function Set.union (a,b)
  local res = Set.new{}
  for k in pairs(a) do res[k] = true end
  for k in pairs(b) do res[k] = true end
  return res
end

function Set.intersection (a,b)
  local res = Set.new{}
  for k in pairs(a) do
    res[k] = b[k]
  end
  return res
end

To help checking our examples, we also define a function to print sets:

function Set.tostring (set)
  local s = "{"
  local sep = ""
  for e in pairs(set) do
    s = s .. sep .. e
    sep = ", "
  end
  return s .. "}"
end

function Set.print (s)
  print(Set.tostring(s))
end

Now, we want to make the addition operator (+) compute the union of two sets. For that, we will arrange that all tables representing sets share a metatable and this metatable will define how they react to the addition operator. Our first step is to create a regular table that we will use as the metatable for sets. To avoid polluting our namespace, we will store it in the Set table:

Set.mt = {}    -- metatable for sets

The next step is to modify the Set.new function, which creates sets. The new version has only one extra line, which sets mt as the metatable for the tables that it creates:

function Set.new (t)   -- 2nd version
  local set = {}
  setmetatable(set, Set.mt)
  for _, l in ipairs(t) do set[l] = true end
  return set
end

After that, every set we create with Set.new will have that same table as its metatable:

s1 = Set.new{10, 20, 30, 50}
s2 = Set.new{30, 1}
print(getmetatable(s1))          --> table: 00672B60
print(getmetatable(s2))          --> table: 00672B60

Finally, we add to the metatable the so-called metamethod, a field __add that describes how to perform the union:

Set.mt.__add = Set.union

Whenever Lua tries to add two sets, it will call this function, with the two operands as arguments.

With the metamethod in place, we can use the addition operator to do set unions:

s3 = s1 + s2
Set.print(s3)  --> {1, 10, 20, 30, 50}

When I tried to run it, I got the result: { union, mt, intersection, tostring, new, print} instead of the numbers in s3. Seems I've printed the contents of the metatables instead. Can someone explain what's happening here? The book describes version 5.0 and I'm using Lua 5.1. Could that be causing this?

hjpotter92
  • 78,589
  • 36
  • 144
  • 183
maxam
  • 159
  • 1
  • 9

1 Answers1

3

There is a bug in the code you ran, not in the code you've posted in the question:

In Set.tostring, line 28, you changed for e in pairs(set) to for e in pairs(Set) and so it always shows the contents of Set, not the contents of the given set.

lhf
  • 70,581
  • 9
  • 108
  • 149
  • Thanks, but that's not it. I've rechecked it, and I wrote Set.print(s3). Any other ideas? – maxam Aug 22 '13 at 11:36
  • That's it! Thank you so much! It's the second time I've had such an annoying bug in lua where I misprint a letter and the whole code becomes screwed up. In my opinion it's because lua is case sensitive and at the same time allows using variables without actually initializing them. – maxam Aug 22 '13 at 12:58
  • 1
    @user2697177, in this case `Set` is initialized. It's not Lua's fault. – lhf Aug 22 '13 at 12:59
  • You're right it's initialized globally..while set is initialized locally so I guess it's a different case – maxam Aug 22 '13 at 13:03
  • 1
    @user2697177 you can use `require 'strict'` so it errors when you use an undeclared global variable. See **[lua wiki - Detecting Undefined Variables](http://lua-users.org/wiki/DetectingUndefinedVariables)**. – greatwolf Aug 22 '13 at 21:10