Attempt to index local 'self' (a number value)

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Superviro
Prole
Posts: 3
Joined: Wed Feb 24, 2021 4:37 am

Attempt to index local 'self' (a number value)

Post by Superviro »

I'm beating my head against the wall on this one.

Code: Select all

require 'libraries.fonts'
Score = {}

function Score:load()
    self.score = 0
end

function Score:update(amount)
    self.score = self.score + amount -- Line of code throwing error
end

function Score:draw()
    love.graphics.setFont(Fonts.dotGothic20)
    love.graphics.print('Score: ' .. self.score, 10, 10)
end

Code: Select all

Error: entities/score.lua:9: attempt to index local 'self' (a number value)
stack traceback:
        [string "boot.lua"]:777: in function '__index'
        entities/score.lua:9: in function 'update'
        state/level.lua:21: in function 'update'
        [string "boot.lua"]:612: in function <[string "boot.lua"]:594>
        [C]: in function 'xpcall'
MrFariator
Party member
Posts: 548
Joined: Wed Oct 05, 2016 11:53 am

Re: Attempt to index local 'self' (a number value)

Post by MrFariator »

This is a mix up between the dot (.) and colon (:) syntax. Consider the following:

Code: Select all

function Score.load(self)
  self.score = 0
end
Score.load(Score)
-- is syntactically equivalent to
function Score:load()
  self.score = 0
end
Score:load()
When you use the colon syntax, you are implicitly sending the table that contains the function you are calling. With the dot syntax you need to explicitly type out the table you wish to send as well. You can mix and match these, but you gotta watch out for problems like this error you are getting now (the 'self' is the number value you are passing). So you have presumably written Score.update(amount) in state/level.lua, instead of Score:update(amount), or Score.update(Score, amount).
User avatar
darkfrei
Party member
Posts: 1197
Joined: Sat Feb 08, 2020 11:09 pm

Re: Attempt to index local 'self' (a number value)

Post by darkfrei »

And how to work with such objects?

Is it the right way? I have troubles with this.

Code: Select all

-- main.lua

Score = require(entities.score)

function love.load()
	scores = {Score:load(), Score:load()} -- players "odd" and "even"
end

function love.update(dt)
	local amount = math.random(1, 2)+math.random(1, 2)
	amount = (amount%2==1) and amount or -amount -- wins odd or even player; 
	scores[1]:update(amount) -- update score for odd
	scores[2]:update(-amount) -- update score for even
end

function draw()
	scores[1]:draw() -- print balance of odd
	scores[2]:draw() -- print balance of even
end
I know that such code is totally wrong, but how to do it the right way?
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
ohai
Prole
Posts: 20
Joined: Thu Jan 21, 2021 9:06 pm

Re: Attempt to index local 'self' (a number value)

Post by ohai »

darkfrei wrote: Wed Feb 24, 2021 1:52 pm Is it the right way?
Seems correct at first glance.

The simplest explanation I can think of:

Code: Select all

function test.x()
  -- you don't have access to "test" here.
end
If you want to access "test" in this function, you can write:

Code: Select all

function test.x(p)
  -- you can pass "test" as parameter "p" to have access to it
end
--for example
result = test.x(test)
Now, you can of course name a parameter as you wish, so take the above example and rename "p" to "self":

Code: Select all

function test.x(self)
  -- you can pass "test" as parameter "self" to have access to it
end
--for example
result = test.x(test)
Writing it this way all the time would be of course quite tedious, so for your convenience lua has a ":" operator:

Code: Select all

function test:x() -- exactly equivalent to    function test.x(self)
  -- this function has a hidden "self" parameter that you can access like every other ordinary parameter.
end
--for example
result = test:x()  -- passes "test" as a hidden parameter, exactly equivalent to    result = test.x(test)
Now, if you were calling a function similar to:

Code: Select all

function test:x()
  [...]
  local example = self.something
  [...]
end
like this:

Code: Select all

test.x()
Then it will throw an error, because in the function definition there is a invisible "self" parameter, and you didn't pass it to the function so it will be nil. To fix this you can change the call to:

Code: Select all

test.x(test)
-- or
test:x()
But the second one is of course preferred if you want to make your code more tidy.
User avatar
darkfrei
Party member
Posts: 1197
Joined: Sat Feb 08, 2020 11:09 pm

Re: Attempt to index local 'self' (a number value)

Post by darkfrei »

This code works: I create two "scores", one is 1, another is 2.
I've tried this with the colon(:) and self, but it doesn't work.

Simplest working code:

Code: Select all

-- test.lua
test = {}
function test.load(score)
	print ('score '..type(score))
	return {score = score or 0}
end

Code: Select all

-- main.lua
require ('test') -- lib
function love.load()
	scores = {test.load(1), test.load(2)} -- players "odd" and "even"
	for i, v in pairs (scores) do
		print (i..' '..type(v)..' score: '..v.score)
	end
end

Code: Select all

score number
score number
1 table score: 1
2 table score: 2
:o Now I need to update them, add and subtract some value to/from them.

Code: Select all

--	function love.update(dt)
	local amount = math.random(1, 2)+math.random(1, 2)
	amount = (amount%2==1) and amount or -amount -- wins odd or even player; 
--	test:update(scores[1], amount) -- update score for odd
How to do it?
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
MrFariator
Party member
Posts: 548
Joined: Wed Oct 05, 2016 11:53 am

Re: Attempt to index local 'self' (a number value)

Post by MrFariator »

If you want to create multiple score tracking objects (assuming a OOP approach, like OP's code), you could do something along the lines of:

Code: Select all

-- score.lua
local Score   = {}
Score.__index = Score

function Score.new ( initialScore ) 
  local newScoreTracker = {}
  newScoreTracker.score = initialScore or 0
  setmetatable(newScoreTracker, Score)
  return newScoreTracker
end

function Score:add ( amount )
  self.score = self.score + amount
end

function Score:print ( title )
  title = title or "Score object "
  print ( title .. " has score:", self.score ) -- note that using ".." like this is not efficient, but this is just sample code
end

return Score
Usage:

Code: Select all

-- main.lua
local Score = require ( "score") -- mind the capitalization, assuming you saved the file next to main.lua 

function love.load ()
  score1 = Score.new()  -- initial score is 0
  score2 = Score.new(2) -- initial score is 2
end

function love.update ()
  -- change score over time
  score1:add(1)   -- add 1 every frame
  score2:add(-1)  -- subtract 1 every frame

  score1:print ( "odd"  )
  score2:print ( "even" )
end
What is going on here is that I create an instance of the Score class with the stuff going on in Score.new, which inherits the methods defined in the class. You can read more about it here, or use one of the many available class libraries. "classic" is a simple and tiny one.

There are other ways to accomplish this, like you could simply write functions that accept tables that contain specific key-pair values (eq. "score") to update them. Depends on your needs and how complex managing them may get.
User avatar
Xii
Party member
Posts: 137
Joined: Thu Aug 13, 2020 9:09 pm
Contact:

Re: Attempt to index local 'self' (a number value)

Post by Xii »

Oh hey this is like the Uniform Function Call Syntax from the D programming language.
Didn't know Lua has it too, thanks for educating me. :)
Superviro
Prole
Posts: 3
Joined: Wed Feb 24, 2021 4:37 am

Re: Attempt to index local 'self' (a number value)

Post by Superviro »

MrFariator wrote: Wed Feb 24, 2021 12:43 pm This is a mix up between the dot (.) and colon (:) syntax. Consider the following:

Code: Select all

function Score.load(self)
  self.score = 0
end
Score.load(Score)
-- is syntactically equivalent to
function Score:load()
  self.score = 0
end
Score:load()
When you use the colon syntax, you are implicitly sending the table that contains the function you are calling. With the dot syntax you need to explicitly type out the table you wish to send as well. You can mix and match these, but you gotta watch out for problems like this error you are getting now (the 'self' is the number value you are passing). So you have presumably written Score.update(amount) in state/level.lua, instead of Score:update(amount), or Score.update(Score, amount).
This of course makes sense to me. However, I'm just trying to reference an instance variable inside of the table function. Should I not access score via self.score? The function is defined with :, so inside of that function I should be able to access self.score.
MrFariator
Party member
Posts: 548
Joined: Wed Oct 05, 2016 11:53 am

Re: Attempt to index local 'self' (a number value)

Post by MrFariator »

You can access the score with self.score, that's perfectly fine. But you need to be mindful of how you invoke the function. To further drill the point, consider the following:

Code: Select all

function Score.update(self, amount)
  self.score = self.score + amount -- the 'self' is explicitly passed as the first parameter
                                   -- 'self' could be named anything here, but 'self' is convention
end
-- is syntactically equivalent to
function Score:update(amount)
  self.score = self.score + amount -- the 'self' is a hidden, first parameter received by the function, 
                                   -- usually a reference to the table, or an instance of a class. 
end

-- both of these calls work, just mind the dot or comma!
Score:update(1234)
Score.update(Score, 1234)
So in your code, based on your original post, you have written:

Code: Select all

Score.update(1234) -- in state/level.lua:21: in function 'update' 
                   -- Here, the number 1234 becomes 'self', and so the code tries to index a number value like it's a table. Error!
                   -- On the other hand, "amount" becomes nil (absence of value), because no second parameter is passed!
When you meant to write

Code: Select all

Score.update(Score,1234) -- explicitly pass the Score table, which will become 'self'
-- or
Score:update(1234) -- Score table becomes 'self' implicitly, this really is the same as writing Score.update(Score,1234)
So in short, lua functions have no concept of knowing what table they are attached to. You need to tell those functions what table it is you want them to be looking at. The dot syntax is basically how lua interprets everything under the hood, the comma syntax giving you a way to reduce the amount of typing involved (ie. it is syntactical sugar).

You can mix and match declaring functions and calling functions with either the dot (.) or comma (:) syntax, but be sure to pass the table as the first parameter if you use the dot, or let lua handle that automatically with the comma syntax.
Superviro
Prole
Posts: 3
Joined: Wed Feb 24, 2021 4:37 am

Re: Attempt to index local 'self' (a number value)

Post by Superviro »

I'm a dope.

The error came from another file where I was calling Score.udpate(1) instead of Score:update(1). I knew it, but I overlooked it.
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Google [Bot], Semrush [Bot] and 6 guests