This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
MrabEzreb's profile picture

function(self)

Started by MrabEzreb, 20 February 2014 - 11:24 AM
MrabEzreb #1
Posted 20 February 2014 - 12:24 PM
How does the "self" argument work? From everything that I have found I still can not figure it out.
Zudo #2
Posted 20 February 2014 - 12:40 PM
With OOP, the self keyword can be used to return an instance of the object in question.
MrabEzreb #3
Posted 20 February 2014 - 12:53 PM
what?
CometWolf #4
Posted 20 February 2014 - 01:36 PM
wow much explaination, such detail.

While im not big on OOP myself, i'll give it a shot. It stands for object oriented programming. In short, this means your code is based around creating template objects with certain attributes, then using these templates to create objects based on those, sharing many of the same characteristics. A good exmple of this, as used in the PIL, is accounts. You create one account template, then whenever you create a new account, they get all the same functions and variable names. This is usually where terms like inheritance come into play, where one object inherites the attributes of another. In LUA, which is not an OOP language by nature, tables can be considered objects as they can contain a multitude of different attributes(variables) aswell as functions and can be used to generate more of itself through inheritance. This is pretty advanced stuff however pertaining to table metamethods and such, which i won't bother getting into.

local objectDef = { -- This is a table, but im going to set it up like an object template
  numb1 = 5,
  numb2 = 10,
  addition = function(self) -- this is where self comes into play, to reference the table this funciton exists in.
	print(self.numb2+self.numb1)
  end,
  subtraction = function(self)
	print(self.numb2-self.numb1)
  end
}
setmetatable(objectDef, {__call = function() -- this makes calling the table result in the following function being invoked, which basically returns a copy of the table.
	local retTable = {}
	for k,v in pairs(objectDef) do
	  retTable[k] = v
	end
	return retTable
  end
})
object = objectDef()
object:addition() --syntactic sugar way of writing object.addition(object)
--prints 15
if you wanna learn more about OOP in lua, go here
http://www.lua.org/pil/16.html

the term self in LUA however, does not have any meaning by default. It's a nil variable like any other, regardless of where you use it. It's just a common variable name for pointing to the object which the function should operate on.

tTable = {
  value = 10,
  multiply = function(selforwhateveryouwannacallit,v)
	return selforwhateveryouwannacallit.value*v
  end
}
print(tTable:multiply(10)) --prints 100
Edited on 20 February 2014 - 12:38 PM
MrabEzreb #5
Posted 20 February 2014 - 01:59 PM

tTable = {
  value = 10,
  multiply = function(selforwhateveryouwannacallit,v)
	return selforwhateveryouwannacallit.value*v
  end
}
print(tTable:multiply(10)) --prints 100
so the first argument could be inserted one of two ways?

doStuff({1, 2, 3}, 1)
--OR
123Table = {1, 2, 3}
123Table:doStuff(1)
CometWolf #6
Posted 20 February 2014 - 02:03 PM
provided dostuff was contained within 123Table, yeah that would work.

123Table = {1, 2, 3, doStuff = function(self,v) for i=1,#self do print(self[i]*v) end end}
123Table:doStuff(1)

Note however that you can't begine a variable name(table) with a number.
Edited on 20 February 2014 - 01:08 PM
MKlegoman357 #7
Posted 20 February 2014 - 04:43 PM
Actually the term self came from Lua itself. If you create a function using : then self is automatically put by Lua:


local object = {text = "Hello"}

function object:print (newText)
  print(self.text)

  self.text = newText or self.text
end

object:print("Test") -->> 'Hello'
object:print() -->> 'Test'

PS: It's Lua, not LUA nor lua.

PS from Bubba: Stop correcting others on the capitalization of Lua. It's not remotely helpful, related to the discussion, or important.
Edited by
CometWolf #8
Posted 20 February 2014 - 05:23 PM
Yes, but this

function object:print (newText)
  print(self.text)
  self.text = newText or self.text
end
and this

function object.print (derpherp,newText)
  print(derpherp.text)
  derpherp.text = newText or derpherp.text
end
Is still the same function, and can be called the same 2 ways

object.print(object,"Test")
object:print()

I suppose you could think of it as a syntatic sugar way of writing table functions, but it has no actual functionailty of it's own.
Edited on 20 February 2014 - 04:23 PM
MrabEzreb #9
Posted 20 February 2014 - 05:38 PM
okay, I am a bit confused, but okay!
ElvishJerricco #10
Posted 20 February 2014 - 06:48 PM
To put it in simple terms, self is a variable that Lua inserts into certain situations. Although its primary use case is OOP, it's a bad idea to explain how it works using the context of OOP. Instead, it's much simpler to understand in the context of functions. For now, don't think of the word self as being special in any way. Just think of it like a normal variable name.


local tbl = {}
tbl.setA = function(self, a)
    self.a = a
end

This bit of code is pretty simple. Create a table, and set its setA value to a function that takes two arguments: self, and a. So now we want to call this function. There are now two ways to do that. The first, I'm sure, you're used to.


tbl.setA(tbl, 3)
print(tbl.a) -- prints 3

Get the setA function out of the table, pass a table into it for the self argument, and pass 3 for the a argument. But what if we don't want to write tbl twice for every call to setA that has tbl as the self argument? That's where colon notation comes in.


tbl:setA(4)
print(tbl.a) -- prints 4

Here's what that colon between tbl and setA does: It tells Lua that the table we're getting the function from should be passed as the first argument to the function and any arguments passed in the written code gets passes as arguments after the table. So in the original function, self was the first argument, meaning tbl was passed to self automatically.


local tbl = {}
function tbl:setA(a)
    self.a = a
end
tbl:setA(3)
print(tbl.a) -- prints 3

There's another example. The difference is that this time, we declared the function using colon notation as well. When declaring a function that way, all it does is puts that first self argument in before the a argument invisibly, and stores the function in the table.

Now, the reason this feature exists is for OOP. Basically, there's various ways to generate objects that have functions which take that self argument. So you want to be able to pass the object in without writing that out.
MrabEzreb #11
Posted 15 March 2014 - 03:10 PM
does the function have to be in the table? or could you do this:

function arg1:myFunction(arg2, arg3)
   arg1 = arg2 + arg3
end
AND/OR
function arg1:myFunction(arg2, arg3)
   arg1.answer = arg2 + arg3
end
Would that work?

EDIT:
If not, would this work?

function arg1:myFunction(arg2, arg3)
   self = arg2 + arg3
end
AND/OR
function arg1:myFunction(arg2, arg3)
   self.answer = arg2 + arg3
end
Edited on 15 March 2014 - 02:12 PM
CometWolf #12
Posted 15 March 2014 - 03:31 PM
You are defining the function, not calling it, so either of those examples would work, provided arg1 is a table.
Calling the functions this way however, prior to defining them, would not work, as using tableName:variableName is a means of calling the variable from the table, then passing the table name itself to the variable. Meaning the variable(function) has to be in the table to begin with. The only way around this is using the __index metatable, which is how OOP is done in Lua.
Edited on 15 March 2014 - 02:32 PM
MrabEzreb #13
Posted 15 March 2014 - 06:03 PM
so if I want to do

function arg1:myFunction(arg2, arg3)
   arg1 = arg2 + arg3
end
with arg1 being something other than a table with myFunction in it, and use it as a normal argument, I have to use an "_index metatable?" what is a metatable?
CometWolf #14
Posted 15 March 2014 - 06:35 PM
a metatable is the action a table uses to perform specific operations. the __index metatable is the one used when you're looking up a key in the table that does not exist. Thus, the table could find your function in another table instead of itself, and be able to use that instead.

local functionTable = {
  func = function(arg1,arg2,arg3)
    print("arg1 is the table ".. arg1.name)
    print("arg2 is "..tostring(arg2))
    print("arg3 is "..tostring(arg3))
  end
}
local test = setmetatable(
  {
    name = "test"
    --the table which im changing the metatable, im creating a new one here instead of creating one and referencing it later
  },
  {
    --the metatable
    __index = functionTable --makes it check functionTable if the key dosen't exist
  }
)
--now the following will work
test:func("derp","herp")

read more here
http://www.lua.org/pil/13.html
MrabEzreb #15
Posted 15 March 2014 - 07:22 PM
a metatable is the action a table uses to perform specific operations. the __index metatable is the one used when you're looking up a key in the table that does not exist. Thus, the table could find your function in another table instead of itself, and be able to use that instead.

local functionTable = {
  func = function(arg1,arg2,arg3)
	print("arg1 is the table ".. arg1.name)
	print("arg2 is "..tostring(arg2))
	print("arg3 is "..tostring(arg3))
  end
}
local test = setmetatable(
  {
	name = "test"
	--the table which im changing the metatable, im creating a new one here instead of creating one and referencing it later
  },
  {
	--the metatable
	__index = functionTable --makes it check functionTable if the key dosen't exist
  }
)
--now the following will work
test:func("derp","herp")

read more here
http://www.lua.org/pil/13.html
so this works:

funcs = {
    function func1(arg1, arg2, arg3)
	    print(arg1.arg4.." "..arg1.arg5.." "..arg1.arg6.." "..arg2.." "..arg3)
    end
}
vars = {
    arg4 = "I am an idiot!",
    arg5 = "I make awesome stuff that has no use!",
    arg6 = "blah blah blah!"
}
setmetatable(vars, {_index = funcs})
--So...
vars.func1("bla", "blay") --I am an idiot! I make awesome stuff that has no use! blah blah blah! bla blay
--OR
vars:func1("bla", "blay") --I am an idiot! I make awesome stuff that has no use! blah blah blah! bla blay
--Colon or period?
CometWolf #16
Posted 15 March 2014 - 07:34 PM
The second one would work, the first one would error.

--this
vars:func1("bla", "blay")
--is the same as
vars.func1(vars,"bla","blay")
This behavior has been adressed at great lengths in this very topic.
MrabEzreb #17
Posted 15 March 2014 - 09:11 PM
OKAY! Now I understand. Can someone give me a link to a list of "metamethods?"
ty!
CometWolf #18
Posted 15 March 2014 - 09:15 PM
http://lua-users.org/wiki/MetatableEvents
theoriginalbit #19
Posted 16 March 2014 - 03:10 AM

123Table = {1, 2, 3}

123Table = {1, 2, 3, doStuff = function(self,v) for i=1,#self do print(self[i]*v) end end}
Note however that you can't begine a variable name(table) with a number.
just so the error message is out there, 'cause it can be quite a confusing one for most people, it would error with for input string: "123Table"

EDIT: Also I'm not sure if it was brought up, so I'll say it just incase, the variable self is only required to be used when you declare a function like so

local foo = {}
function foo:bar( x,y )
  --# there are 3 arguments: self, x, and y
  --# `self` would be a reference to `foo` when invoked like foo:bar(5,5)
  --# however the function could still be invoked like foo.bar(foo,5,5) meaning that you could provide another table if you wished like foo.bar({}, 5, 5)
end
you can call self whatever you want by defining a function like so

local foo = {}
function foo.bar( this, x, y )
  --# there are 3 arguments: this, x, and y
  --# `this` would be a reference to `foo` when invoked like foo:bar(5,5)
  --# the same rules apply to this function as do with the previous, the previous is just simply syntactical sugar
end
Edited on 16 March 2014 - 02:15 AM
MrabEzreb #20
Posted 28 May 2014 - 08:32 PM
Just in case anyone would still answer now that i get the point of OOP, by doing
function table:func()
The function table.func() or table:func() would give the function an argument called "self" which would contain everything in "table", right?
KingofGamesYami #21
Posted 28 May 2014 - 08:55 PM
I don't think it contains the table, rather it is the table.
MrabEzreb #22
Posted 28 May 2014 - 08:58 PM
Right. But which would work to call the function, table.func() and/or table:func()?
KingofGamesYami #23
Posted 28 May 2014 - 09:03 PM
table:func( argument )
is exactly the same as
table.func( table, argument )
Using the ":" somehow projects the self variable into the other table. Both would work to call the function, but table:func( argument ) is prettier.
MrabEzreb #24
Posted 28 May 2014 - 09:37 PM
Okay. Ty! But also, how does something like the window api work? It seems like it returns an "object", but it uses a period. How i do dat?
apemanzilla #25
Posted 28 May 2014 - 09:44 PM
Just in case anyone would still answer now that i get the point of OOP, by doing
function table:func()
The function table.func() or table:func() would give the function an argument called "self" which would contain everything in "table", right?
Correct. The first argument passed will be the object prior to the colon. This is why you can use "str:rep(2)" where str is a string.
MrabEzreb #26
Posted 28 May 2014 - 09:48 PM
Okay. But how do things like the window api work?
CometWolf #27
Posted 28 May 2014 - 10:34 PM
It defines the object within the creation function, then defines the variables inside the object, before returning it. It's heavier computation wise, as the variables have to be defined for each object, but it's more newb friendly i suppose.

local new = function()
  local window = {
	x = 1,
	y = 1,
	resize = function(x,y)
	  window.x = x
	  window.y = y
	end
  }
  return window
end
Edited on 28 May 2014 - 08:34 PM
MrabEzreb #28
Posted 28 May 2014 - 10:57 PM
Okay. I think that that would be both easier to define AND to call. Ty!
EDIT:
Do i have to define the object WITHIN the function, or could it be somewhere else in the api?
Edited on 28 May 2014 - 08:59 PM
CometWolf #29
Posted 28 May 2014 - 11:10 PM
You could, but if you're not creating it within a function there's really no point, since it won't have it's own instance. You might aswell ditch the function altogether and store it directly in your api.
Edited on 28 May 2014 - 09:10 PM
MrabEzreb #30
Posted 28 May 2014 - 11:12 PM
Okay, so your example would have to be "window = new()". So how does the window api work?
CometWolf #31
Posted 28 May 2014 - 11:14 PM
It works just like that…

local screenWindow = window.create()
MrabEzreb #32
Posted 28 May 2014 - 11:31 PM
Oh, i thought you just said that it wouldn't.
CometWolf #33
Posted 28 May 2014 - 11:33 PM
I responded to this
Do i have to define the object WITHIN the function, or could it be somewhere else in the api?
with
You could, but if you're not creating it within a function there's really no point, since it won't have it's own instance. You might aswell ditch the function altogether and store it directly in your api.
Where does the window API enter the picture?
Edited on 28 May 2014 - 09:33 PM
KingofGamesYami #34
Posted 28 May 2014 - 11:58 PM
I responded to this
Do i have to define the object WITHIN the function, or could it be somewhere else in the api?
with
You could, but if you're not creating it within a function there's really no point, since it won't have it's own instance. You might aswell ditch the function altogether and store it directly in your api.
Where does the window API enter the picture?

He was using this post:
It defines the object within the creation function, then defines the variables inside the object, before returning it. It's heavier computation wise, as the variables have to be defined for each object, but it's more newb friendly i suppose.

local new = function()
  local window = {
	x = 1,
	y = 1,
	resize = function(x,y)
	  window.x = x
	  window.y = y
	end
  }
  return window
end
Where you had the function new, in which case he is correct.

newobj = new()
You guys just had a miscommunication.
MrabEzreb #35
Posted 29 May 2014 - 12:33 AM
Again, how would i make an object like the window API does? Without the colen.
CometWolf #36
Posted 29 May 2014 - 12:41 AM
Okay. But how do things like the window api work?
It defines the object within the creation function, then defines the variables inside the object, before returning it. It's heavier computation wise, as the variables have to be defined for each object, but it's more newb friendly i suppose.

local new = function()
  local window = {
	x = 1,
	y = 1,
	resize = function(x,y)
	  window.x = x
	  window.y = y
	end
  }
  return window
end
KingofGamesYami #37
Posted 29 May 2014 - 03:45 AM
To avoid confusion, I'm going to make two examples.

: notation example

funcs = {
  add = function( self, num )
   self.value = self.value + num
  end,
  subtract = function( self, num )
   self.value = self.value - num
  end,
}
function new( num )
  local t = {value = num}
  setmetatable(t, funcs)
  return t
end

. notation

function new( num )
  t = {
    value = num,
  }
  t.add = function( num )
   t.value = t.value + num
  end
  t.subtract = function( num )
   t.value = t.value - num
  end
  return t
end
Edited on 29 May 2014 - 03:16 AM
Lyqyd #38
Posted 29 May 2014 - 04:03 AM
You can't reference t from within t.


function new(num)
  local t = {}
  t.value = num
  t.add = function(num)
    t.value = t.value + num
  end
  return t
KingofGamesYami #39
Posted 29 May 2014 - 05:15 AM
You can't reference t from within t.


function new(num)
  local t = {}
  t.value = num
  t.add = function(num)
    t.value = t.value + num
  end
  return t
Ah yes, I had forgotten about that. Updating post…