Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
0

Metatable/Object Oriented Programming help?

Asked by
Turgon 80
8 years ago

Im trying to understand how metatables work and what object oriented programming actually is. I've encountered something i can't figure out.

Account = {}

function Account.new(value)
    return setmetatable({balance = value}, Account)
end
Account.__index = Account --THIS LINE

function Account:withdraw(v)
    self.balance = self.balance - v
end
function Account:deposit(v)
    self.balance = self.balance + v
end
function Account:__tostring()
   return "Account(" .. self.balance .. ")"
end

b = Account.new(100)
b:deposit(10)
print(b.balance)

This is copy pasted from the Roblox Wiki. I can't figure out what the 6th line is for, the script doesn't work without it and the error code says "Workspace.Script:21: attempt to call method 'deposit' (a nil value)" so im completely lost. If anyone could explain to me how this script exactly works i'd be grateful.

1 answer

Log in to vote
1
Answered by
Unclear 1776 Moderation Voter
8 years ago

A quick glance can mislead you into thinking that Account is the object when it is not. Line 4 creates a new object, because setmetatable returns its first argument. The only defined key in this object is "balance" as you can see here {balance = value}.

This makes for a very boring table. Thankfully, this table has a metatable. The details of this metatable have been outlined in the surrounding lines. The variable Account points to this metatable.

Now, our table that we created before can access deposit and withdraw only because of line 6. The __index metamethod by default will make the table search for contents in the metatable itself if a key cannot be found within the table.

Really sorry that you were confused by this. This was a horrible example ripped right out of a page from the Lua website. In reality, your code should separate your constructor, object table, and metatable. This example fails to do that by combining constructor and metatable, as well as throwing some methods that could have just been defined in the object table right in the metatable. People often do this because it is more memory efficient, and to that I laugh because working solely in a scripting language means you have nearly no right or need to fuss over such petty optimization details.

Here's is a cleaned up, and arguably easier to understand, implementation of the sample you shared. I made sure to clearly separate constructor, object table, and metatable so it should be much easier to learn from it.

local Account = { }
local Account_Metatable = {
  __tostring = function(self)
    return "Account(" .. self.balance .. ")"
  end
}

function Account.new(value)
  local Object = {balance = value}
  function Object:withdraw(v)
    self.balance = self.balance - v
  end
  function Object:deposit(v)
      self.balance = self.balance + v
  end
  return setmetatable(Object, Account_Metatable)
end

b = Account.new(100)
b:deposit(10)
print(b.balance)

0
Looking at this, other scripts and youtube tutorials at the same time to understand how they really connect together. Only getting more confused :s Thanks for the well-written reply. I noticed the _tostring has no effect on the script, it works without it aswell. Im really failing at understanding this, the only place Account is used is Account.new, i'm not even sure if they're related in anyway. Turgon 80 — 8y
0
Account exists as a table to hold a constructor. The general idea is sometimes there may be functions independent of individual objects (like creating objects, for example with "new"). Unclear 1776 — 8y
0
As for your __tostring comment, that's more of a metamethod thing. It appears you don't quite understand those. __tostring exists here so you can do print(b) and get a formatted output, or concatenate it. Unclear 1776 — 8y
0
I can just replace the setmetatable part with "return Object", and it pretty much does the same thing. What's the point of the metatable in the first place? Turgon 80 — 8y
View all comments (3 more)
0
YonaJune's code creates a new method for each and every object that you create. Your original code *does not* -- which is what the metatable is for. The metatable approach is a little "safer" -- you can add checks that will error if you try to grab something that doesn't exist, and you can prevent the methods from being redefined. BlueTaslem 18071 — 8y
0
BlueTaslem is right. I am a strong believer that you shouldn't start using that approach until you've really grasped metatables, though. You should read up on those first before tackling OOP. You will understand both sides rather well if you do that rather than being clueless. Unclear 1776 — 8y
0
Thanks a lot guys Turgon 80 — 8y
Ad

Answer this question