Jump to content




OOP in Lua


53 replies to this topic

#1 RunasSudo-AWOLindefinitely

  • Signature Abuser
  • 249 posts
  • Location/dev/earth1aus5

Posted 08 January 2013 - 05:37 PM

OOP in Lua

Introduction to OOP
OOP (Object-oriented programming) is a style of computer programming that represents concepts as “objects” that have data fields (attributes that describe the object) and associated functions known as methods.

If you didn’t know that, this tutorial probably isn’t for you (this tutorial assumes at least some knowledge about OOP).

OOP in Lua
Lua does not have any OOP functionality, but one of its features – metatables – can be used to simulate OOP. A metatable in Lua is a table which describes another table (table-ception? no?)

Let’s Go!
Note: In this tutorial, I’ll be using Java terminology such as “class” and “constructor”. Just to clarify.
In this tutorial, we’ll be creating a “class” called Person, which has one field, name, three methods, getName(), setName(name) and toString(), and one constructor, new().

First off, we will define the Person “class”.
Person = {}
Person_mt = {}
Classes in Lua are represented by tables. The Person_mt table is a metatable which we will be using to describe the Person objects.

Now, we will define the setName, getName and toString methods.
function Person.setName(self, newName)
	self.name = newName
end

function Person.getName(self)
	return self.name
end

function Person.toString(self)
	return "Person object. name=" .. self.name
end 
But wait! What’s this mysterious self variable? Don’t worry. All will become clear later.

Now, we will define the the class constructor.
function Person.new()
	local personObject = { name = "" }
	setmetatable(personObject, Person_mt)
	return personObject
end
Okay, seriously, what is going on here? Don’t worry. I’ll go through these bits once we’re finished.

Finally, we need to set up the Person_mt metatable to do what we want.
Person_mt.__index = Person

At this point (after some reshuffling), the code looks like
Person = {}
Person_mt = {}

Person_mt.__index = Person

function Person.new()
	local personObject = { name = "" }
	setmetatable(personObject, Person_mt)
	return personObject
end
function Person.getName(self)
	return self.name
end
function Person.setName(self, newName)
	self.name = newName
end
function Person.toString(self)
	return "Person object. name=" .. self.name
end
Let’s go through this, section by section.

Person_mt.__index = Person
Like I said before, a metatable is a table which describes a table. When I call a function or access a field of a table, Lua checks the __index field of the associated metatable. This line tells Lua that when I call personObject:something, it should go to the Person table and look up something.

function Person.new()
	local personObject = { name = "" }
	setmetatable(personObject, Person_mt)
	return personObject
end
This code defines a function new() within the Person table. Writing Person.new is the same as writing Person["new"]. Just a little syntactic sugar. Firstly, it creates a new table, personObject, with a name field. Then it tells Lua which metatable to use for the personObject table. Finally, it returns the personObject table which now acts as an object.

function Person.getName(self)
	return self.name
end
When we call this function, we can either call Person.getName(personObject) or personObject:getName(). Notice the colon instead of full-stop in the second call. This tells Lua to find the getName function in Person_mt.__index and pass personObject as the first parameter.

The rest of the functions should be self-explanatory.

Now for some test code!
local personObject = Person:new()
personObject:setName("John")
print(personObject:toString())
Output:
Person object. name=John

OOP Pro Tip
String objects are also defined in this way, so instead of writing string.sub(variable, 5) you can write variable:sub(5).


Thank you for reading to the end of my somewhat messy explanation. Please leave constructive criticism in the comments (replies, whatever).

#2 theoriginalbit

    Semi-Professional ComputerCrafter

  • Moderators
  • 7,332 posts
  • LocationAustralia

Posted 08 January 2013 - 05:41 PM

Nice tutorial! One typo tho, one too many end ;)

View PostRunasSudo, on 08 January 2013 - 05:37 PM, said:

Now, we will define the the class constructor.
function Person.new()
	local personObject = { name = "" }
	setmetatable(personObject, Person_mt)
	return personObjectend
end


#3 Orwell

    Self-Destructive

  • Members
  • 1,091 posts

Posted 08 January 2013 - 06:03 PM

Good tutorial. I like it that you actually explain stuff rather than listing some functions, like I've seen lately.. (Not that you could possible just 'list' OOP anyway :P)

I have 2 comments:
1. Not important at all, but it's kinda useless to use Person_mt as the metatable when it's just a reference to the Person anyway. I usually do this:
Person = {}
Person.__index = Person
then later on:
setmetatable(person_object, Person)
It is literally the same thing, but Person is the metatable, so why changing its name? :)

2. Not completely in the scope of OOP, but maybe handy to know.. You can define the field .__tostring as a member function return the string representation. print() will use tostring() which searches for this member function to retrieve a string representation of the table.
So if you'd add this:
function Person.__tostring(self)
  return self:toString()
end
Then you could print the person object with:
print(personObject)
It does also provide a nice example of function ("operator" in some contexts) overloading. :)

#4 RunasSudo-AWOLindefinitely

  • Signature Abuser
  • 249 posts
  • Location/dev/earth1aus5

Posted 08 January 2013 - 06:23 PM

View PostTheOriginalBIT (OnHoliday), on 08 January 2013 - 05:41 PM, said:

Nice tutorial! One typo tho, one too many end ;)
Whoops! Thanks for pointing that out!

View PostOrwell, on 08 January 2013 - 06:03 PM, said:

1. Not important at all, but it's kinda useless to use Person_mt as the metatable when it's just a reference to the Person anyway.
2. Not completely in the scope of OOP, but maybe handy to know.. You can define the field .__tostring as a member function return the string representation.
1. I mainly did it to make it less confusing and to reinforce that the metatable is (usually) a separate table. Does that make sense? :P
2. I know that (I didn't read the Lua reference manual for nothing :)). I just felt like including another method.

#5 theoriginalbit

    Semi-Professional ComputerCrafter

  • Moderators
  • 7,332 posts
  • LocationAustralia

Posted 08 January 2013 - 06:24 PM

View PostOrwell, on 08 January 2013 - 06:03 PM, said:

2. Not completely in the scope of OOP, but maybe handy to know.. You can define the field .__tostring as a member function return the string representation. print() will use tostring() which searches for this member function to retrieve a string representation of the table.
So if you'd add this:
function Person.__tostring(self)
  return self:toString()
end
Then you could print the person object with:
print(personObject)
It does also provide a nice example of function ("operator" in some contexts) overloading. :)

Expanding on that... there are more here... http://lua-users.org...MetatableEvents

#6 RunasSudo-AWOLindefinitely

  • Signature Abuser
  • 249 posts
  • Location/dev/earth1aus5

Posted 08 January 2013 - 06:28 PM

View PostTheOriginalBIT (OnHoliday), on 08 January 2013 - 06:24 PM, said:

View PostOrwell, on 08 January 2013 - 06:03 PM, said:

2. Not completely in the scope of OOP, but maybe handy to know.. You can define the field .__tostring as a member function return the string representation. print() will use tostring() which searches for this member function to retrieve a string representation of the table.
So if you'd add this:
function Person.__tostring(self)
  return self:toString()
end
Then you could print the person object with:
print(personObject)
It does also provide a nice example of function ("operator" in some contexts) overloading. :)

Expanding on that... there are more here... http://lua-users.org...MetatableEvents
More here (where I learnt about it): Lua Reference Manual 5.0 :P

#7 theoriginalbit

    Semi-Professional ComputerCrafter

  • Moderators
  • 7,332 posts
  • LocationAustralia

Posted 08 January 2013 - 06:33 PM

View PostRunasSudo, on 08 January 2013 - 06:28 PM, said:

View PostTheOriginalBIT (OnHoliday), on 08 January 2013 - 06:24 PM, said:

View PostOrwell, on 08 January 2013 - 06:03 PM, said:

2. Not completely in the scope of OOP, but maybe handy to know.. You can define the field .__tostring as a member function return the string representation. print() will use tostring() which searches for this member function to retrieve a string representation of the table.
So if you'd add this:
function Person.__tostring(self)
  return self:toString()
end
Then you could print the person object with:
print(personObject)
It does also provide a nice example of function ("operator" in some contexts) overloading. :)

Expanding on that... there are more here... http://lua-users.org...MetatableEvents
More here (where I learnt about it): Lua Reference Manual 5.0 :P
Same content. Yours was just a find the page and read, vs mine which was read :P

EDIT: I guess the document does have a little better explanation.

#8 Orwell

    Self-Destructive

  • Members
  • 1,091 posts

Posted 08 January 2013 - 06:38 PM

View PostRunasSudo, on 08 January 2013 - 06:23 PM, said:

1. I mainly did it to make it less confusing and to reinforce that the metatable is (usually) a separate table. Does that make sense? :P
No, I don't really get it. :P I know it's done in this way more often, but it's not a separate table. Not in any case of OOP I witnessed anyway. If you have a counter example, please show. :) Anyway, it's not that important. :P

#9 RunasSudo-AWOLindefinitely

  • Signature Abuser
  • 249 posts
  • Location/dev/earth1aus5

Posted 08 January 2013 - 06:44 PM

View PostOrwell, on 08 January 2013 - 06:38 PM, said:

View PostRunasSudo, on 08 January 2013 - 06:23 PM, said:

1. I mainly did it to make it less confusing and to reinforce that the metatable is (usually) a separate table. Does that make sense? :P
No, I don't really get it. :P I know it's done in this way more often, but it's not a separate table. Not in any case of OOP I witnessed anyway. If you have a counter example, please show. :) Anyway, it's not that important. :P
I just thought it might be a little confusing. "It's its own metatable, which means it describes itself, so when you call something in it, it looks up its metatable... which is itself... and finds the __index field... which is itself.. and then.. calls itself...?" Not a big deal either way.

"this.setmetatable(this, this)"
Whaaaa???

#10 Orwell

    Self-Destructive

  • Members
  • 1,091 posts

Posted 08 January 2013 - 07:38 PM

View PostRunasSudo, on 08 January 2013 - 06:44 PM, said:

View PostOrwell, on 08 January 2013 - 06:38 PM, said:

* snip *
I just thought it might be a little confusing. "It's its own metatable, which means it describes itself, so when you call something in it, it looks up its metatable... which is itself... and finds the __index field... which is itself.. and then.. calls itself...?" Not a big deal either way.

"this.setmetatable(this, this)"
Whaaaa???
Well, it isn't its own metatable... Person/Person_mt is the metatable of personObject. And in the end, __index of both of those is Person. So Person is the metatable, and Person_mt is some sort of inherited object/table of Parent with the same properties. So in my opinion, it's just a synonym. But I guess it's just a matter of favor.

#11 RunasSudo-AWOLindefinitely

  • Signature Abuser
  • 249 posts
  • Location/dev/earth1aus5

Posted 08 January 2013 - 08:25 PM

View PostOrwell, on 08 January 2013 - 07:38 PM, said:

Well, it isn't its own metatable... Person/Person_mt is the metatable of personObject. And in the end, __index of both of those is Person. So Person is the metatable, and Person_mt is some sort of inherited object/table of Parent with the same properties. So in my opinion, it's just a synonym. But I guess it's just a matter of favor.
See, even I'm confusing myself :P

Yay! I'm a Scripter now!

#12 ChunLing

  • Members
  • 2,027 posts

Posted 09 January 2013 - 12:09 AM

I have to admit, I'm...I've never really understood the point of object oriented programming. I mean, I totally get having different data types, but I don't get packing them into objects and then only interacting with those objects according to certain methods.

Perhaps it's because that's not at all how I approach real world objects. I'm totally comfortable taking them apart and arranging their basic elements to suit my current needs, and totally uncomfortable with things that are made to be difficult to disassemble and modify according to the situation. Of course, it's a two steps forward one step back kinda process. I guess that for every thousand dollars of time, effort, and money I save, I gotta blow at least a couple hundred somewhere by tinkering with something I shouldn't have tampered with. But usually that's because some idiot tried to make it tamper-proof.

That's what programming objects feel like to me, tamper-proofing. Tamper-proofing is for bombs and other front-end weapons, so that the enemy doesn't disarm them and use the components against you somehow.

Does that make me weird? Probably a little.

#13 Orwell

    Self-Destructive

  • Members
  • 1,091 posts

Posted 09 January 2013 - 12:20 AM

I think that you are missing the point of object orientation. You can have a high level of abstraction and 'disassemblement' in OOP. Whilst most people see it as representing physical object as types, it doesn't have to be that way. There's a lot of theory that bridges modelling and programming. One of the major aspects is the link between concepts and types. These concepts can be very abstract and so can be the types derived from it. Benefits of OOP are abstraction, modularity (easy for debugging and updating), responsibilities (each object has a well defined task) and a lot more I can't think of right now.

#14 RunasSudo-AWOLindefinitely

  • Signature Abuser
  • 249 posts
  • Location/dev/earth1aus5

Posted 09 January 2013 - 12:51 AM

View PostChunLing, on 09 January 2013 - 12:09 AM, said:

I have to admit, I'm...I've never really understood the point of object oriented programming.
Okay, so I want to represent a human in a data structure, but I know that later, I may add more things, so, I set up my classes as follows:
Human (is a) Biped (is an) NPC (is an) Entity
Now, anything a Biped can do (walk()) can be done by a Human, and anything an NPC can do can be done by a Human and so on.

If I wanted to do this in a procedurally oriented program......... Umm................ Metatables?
Writing extends is just easier than copying over functions or doing crazy stuff with (already crazy) metatables...

Now you've probably got a pre-prepared rebuttal in your head from reading this common example.

#15 darkroom

  • Members
  • 70 posts

Posted 09 January 2013 - 07:12 AM

So could you give some more examples of OOP in Lua for example could you make a class that makes instances of drop down menus or something?

#16 darkroom

  • Members
  • 70 posts

Posted 09 January 2013 - 07:13 AM

Edit: Sorry double post

#17 ChunLing

  • Members
  • 2,027 posts

Posted 09 January 2013 - 07:14 AM

Not pre-prepared, but yeah...that's not how I view humans. Maybe it's how I should view humans (certainly, according to some).

I mean...probably it's not a good thing that I don't automatically discard the notion of adjusting someone's attitude with a screwdriver, but I don't. It's that simple, objects as they are defined by others tend to frustrate my native problem solving processes. Probably objects as they would be defined by me would be conceptually useless to others.

I guess I might actually like objects if i thought using a conceptual framework shared by more people.

#18 theoriginalbit

    Semi-Professional ComputerCrafter

  • Moderators
  • 7,332 posts
  • LocationAustralia

Posted 09 January 2013 - 07:16 AM

an example is the vector api. have a read of that. and yes you could technically make drop down menus objects. you could make windows objects too. or buttons. or in my case I'm currently attempting to convert my Extended String Library to OO to make it easier for the programmer to implement and use.

#19 Orwell

    Self-Destructive

  • Members
  • 1,091 posts

Posted 09 January 2013 - 07:38 AM

View PostChunLing, on 09 January 2013 - 07:14 AM, said:

Not pre-prepared, but yeah...that's not how I view humans. Maybe it's how I should view humans (certainly, according to some).

I mean...probably it's not a good thing that I don't automatically discard the notion of adjusting someone's attitude with a screwdriver, but I don't. It's that simple, objects as they are defined by others tend to frustrate my native problem solving processes. Probably objects as they would be defined by me would be conceptually useless to others.

I guess I might actually like objects if i thought using a conceptual framework shared by more people.

View PostTheOriginalBIT, on 09 January 2013 - 07:16 AM, said:

an example is the vector api. have a read of that. and yes you could technically make drop down menus objects. you could make windows objects too. or buttons. or in my case I'm currently attempting to convert my Extended String Library to OO to make it easier for the programmer to implement and use.
That is a perfect example. You really don't need to limit OOP to representing physical objects and obvious relations. Objects are defined by behaviour, state and something else I can't remember. The state of a vector is the coordinates and the behavior is the combination of all operators. Using a procedural paradigm for vectors would be really not worth it.

#20 ChunLing

  • Members
  • 2,027 posts

Posted 09 January 2013 - 07:44 AM

Method. Interaction methods. And yeah, that's the exact sticking point for me.





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users