Trying to understand more about Method, Self, and Tables which contain functions (OOP?)

Hi all,

Not sure if I should ask these questions in Newbie questions forum or here. Since I have studied very hard to figure out my confusions, I decide to ask here. For your convenience, I will describe what I learned so far, and then ask my questions after.

What I have known:
When I google articles and study around youtube tutorials to understand the meanings of self, methods, and the differences between dot/colon operators, I noticed they often mentioned OOP(Object-oriented programming). Lua is my first program-like language I have ever learned, so it’s hard for me to understand the meaning of OOP. According to what I read/watch so far, I “feel” OOP is something very like copy/instance/reference of an object in 3ds max (https://youtu.be/2E5DDduuVb4, sorry this video is in Chinese, but I cannot find any shorter and clear one in English.) Basically it’s saying, a copy of an object, any further modification is independent from the original one. The instance of an object will be treated as the same. No matter applying the further modification on either the instance or the original, both will be changed. The reference of an object has the parent-child relationship, the original object is the father and the reference is the child. The further modification on child won’t effect the father. The further modification of father will effect the child.

Table is an object in Lua. "This allows a table, that has been passed into a function, to be changed directly unlike other types that are always passed as a copy of the original. The table type is the only type that passes by reference. " (–> This is from the tutorial https://github.com/GuildSA/corona-hack-pack/blob/master/LuaSamples/lua_6_reference_vs_copy.lua ) This tutorial make sense to me. I can imagine a table goes into a function “factory” as a reference, be modified, and then came out with different content/data inside it.

The method is a function inside the table.

The other studies related to my questions are these:
https://stackoverflow.com/questions/4911186/difference-between-and-in-lua

Lua: Self Tutorial by John Lindquist  https://youtu.be/F-xJq6s6lK0
 

I am going to ask my confusion with John Lindquist’s youtube code.

local dog ={} function dog:speak() print("bark") end function dog:reactToDoorbell() self:speak() end dog:reactToDoorbell() --[[for k,e in pairs(dog) do print ( "table["..k.."]" .." is ".. e) end ]] print(dog.speak) print(dog.reactToDoorbell) print(dog[1]) print(dog[2])

Somehow I cannot make the code line number to show up, so please let me ask questions in screenshots below.

My questions are the circle numbers in screenshots 1, 2, 3:

Q1. Does this mean we add two functions in the table dog? One is speak(), another is reactToDoorbell() ?

Q2. However, why can’t I print out the values with pairs to prove they are added?

Q3. The error message in the output panel, does it mean the functions are added? Why I cannot print them out?

iIYqKUC.png

Q4. Later I found out I can print them out individually. Therefore, is it a correct idea to consider that, 2 functions are added by line 3~9 ?
Also, we can say, these 2 functions are methods?

HySCv30.png

Q5: I am trying to keep the same concept and re-writing code into this way. Why doesn’t it work?

local dogSpeak = function() print("bark in function") end local reactToDoorbell = function() self:speak() end -- dog = {a="inside dog table", speak = dogSpeak, reactToDoorbell = function() self:speak() end} dog = {a="inside dog table", speak = dogSpeak, run = reactToDoorbell} print(dog.speak()) --\> bark in function print(dog.speak) --\> something like function: 0000000000528DA0 print(dog.run) --\> something like function: 0000000000528E30 print(dog.run()) print(dog:run()) 

KE5yjii.png
Thank you in advance to check out my questions. I am looking forward to your thoughts.

I would amend your analysis slightly in that an instance doesn’t need to be copy (in which case the original would be a prototype ) but may instead be a reification (“made into a thing”) of an idea (which will go by names like class ). Modifying your earlier example:

local dog ={} -- our Platonic ideal of a dog function dog:speak() print("bark") end function dog:reactToDoorbell() self:speak() end dog.\_\_index = dog -- find our instances' method names here local fido = setmetatable({}, dog) -- instance #1... local spike = setmetatable({}, dog) -- ...and #2 fido:speak() spike:speak()

Q1 : Yes.

Q2 - Q4 : See your error message. Basically, the “…” operation is expecting strings, but the e values are functions. You can tostring(e) them to make it work, or just comma-separate the call a bit, since print() can take multiple arguments: say as print ( “table[”…k…"]" …" is ", e). Formatting in the console might be a little weird, but otherwise fine.

Q5 : You will only get an implicit self variable when using the colon syntax to define the function. However, it’s perfectly fine to write reactToDoorbell = function(self) and it will give you practically identical code.

here’s a sample Dog class, written in a style that “exposes” the self parameter, then calls it in various ways to hopefully illustrate what’s going on, if it helps…

local Dog = { new = function(self,base) return setmetatable(base or {},self) end, speak = function(self) print("I am a dog named " .. (self.name or "Unnamed") .. " and I say '" .. (self.voice or "Silence") .."'") end } Dog.\_\_index = Dog -- have the Class do something (akin to "static methods") Dog:speak() -- Dog is implicitly "self" Dog.speak(Dog) -- Dog is explicitly "self" -- create some instances and have THEM do something local rover = Dog:new({name="Rover", voice="woof"}) rover:speak() -- rover is implicitly "self" rover.speak(rover) -- rover is explicitly "self" local rufus = Dog:new({name="Rufus", voice="BARK"}) rufus:speak() -- rufus is implicitly "self" rufus.speak(rufus) -- rufus is explicitly "self" -- have the class do something on an instance local roxie = Dog:new({name="Roxie", voice="bowwow"}) Dog.speak(roxie) -- roxie is explicitly "self" -- works same, even though called via Dog, b/c it's the same fn! only "self" changes -- this is equiv to what the metatable does behind the scenes when call roxie:speak()

[edited:  code fell out of code box when posted]

P.S. (nitpick) “The table type is the only type that passes by reference” is incorrect

:open_mouth: :open_mouth: :open_mouth: !!

“The table type is the only type that passes by reference” is incorrect. ==> Oops! So, well … What is correct?

Although the author, who I quote from, turned off his tutorial channels (youtube), I would like him to know that he made very awesome Lua and Corona SDK tutorials. :wub:  (In case he sees this post… OMG! I don’t mean to dirty his reputation. I am very sorry. :ph34r:)


Hi hi! StarCrunch and davebollinger,

Thank you for taking time to answer my questions.  When I see setmetatable and double underscores, I think they are related to metamethod, right?  I haven’t watched related tutorials yet because I thought they were too hard and not using in Corona.(?) Therefore I will need time to digest both of your answers. Overall, I got more confident of my study by getting your replies. Thank you!

So, just want to make sure, looking for metamethod tutorial is a right direction?

I will be aware of “instance” during tutorial search.
 

 

  Oh boy…

  Most important omission is functions.  But in truth it’s more complicated than that (which is why I didn’t go into it, but you asked, so…).  If we want to use the terms correctly, then technically everything in Lua is passed by value.  Always.  However, for tables, functions, threads, and userdata, the value that is passed is a reference.  (you could even argue that constant string values are also references, though this is merely an internal implementation detail)

  So if you use the term “loosely” (ie, incorrectly) then you could say “tables (et al) are passed by reference”, though they’re really not.  (a way to sort of “prove” this to yourself is by the fact that there is no “de-reference” operator in Lua)  The real answer is that nothing is passed by reference.  Ever.  But references _ are _ passed, by value.

   It’s a subtle distinction that probably only matters to those coming from other languages where true pass-by-reference exists. (if you’ve never dereferenced a “pointer-to-a-pointer” or used a “&” parameter, then you probably won’t get it)

A = { name="I am table A" } B = { name="I am table B" } function swap(t1, t2) -- if t1,t2 were REFERENCES to A,B -- then I could reassign A and B in the OUTER scope -- via these local references: t1,t2 = t2,t1 -- but it doesn't work, it only swapped the locals: print("t1,2=", t1.name, t2.name) -- because t1,t2 are local copies of the VALUE of those table references end -- swap(A, B) print("A,B=", A.name, B.name) 

try this (the entire chapter, not just first page):  https://www.lua.org/pil/16.html

:ph34r:  Arrrrhhh … OK … Thank you for your explanation. Things are getting out of my expectation.

OK, I will try to study more… for making apps with Corona… Arrrrrhhh… :unsure:

At least I got a direction to go. Thanks again!

For a pet project, I’ve being messing with this: https://github.com/davporte/classy/ . Would love others to build on it.

Not exactly doing it natively, but thought this was a good example to run with it.

[lua]local classy = require (‘classy’)

– A base Animal class that contains the name and the voice

local Animal = classy:newClass( 

  classy:attributes ( { name = String ( Public ), voice = String ( Public ) } ),

  classy:addMethod (‘speak’,

      function ( obj, animal_type )

        print ( 'I am a ’ … animal_type … ’ breed ’ … ( obj.name or ‘Unnamed’) … ',

                  and I say ’ … ( obj.voice or ‘Silence’) )

        end

   )

)

– Dog inherits from Animal  

local Dog = classy:newClass( Animal,

  classy:addMethod (‘speak’,

    function ( obj )

      classy:callSuperMethod ( obj, obj.super.speak, ‘dog’ )

    end

  )

)

– Cat inherits from Animal  

local Cat = classy:newClass( Animal,

  classy:addMethod (‘speak’,

    function ( obj )

      classy:callSuperMethod ( obj, obj.super.speak, ‘cat’ )

    end

  )

)

local rover = Dog ( { name = ‘Rover’, voice = ‘woof’ } )

local rufus = Dog ( { name = ‘Rufus’, voice = ‘bark’ } )

local pebbles = Cat ( { name = ‘Pebbles’, voice = ‘meow’ } )

rover:speak ()

rufus:speak ()

pebbles:speak ()[/lua]

Output will be;

I am a dog breed Rover, and I say woof

I am a dog breed Rufus, and I say bark

I am a cat breed Pebbles, and I say meow