Methods and self

In farm.describe(farm), you write farm twice: once to find the function, and once to give it the farm. Objects are used like this all the time, so Lua has a shortcut, the colon:

local farm = {name = "Sunflower Farm", coins = 120}

function farm.describe(f)
    print(f.name .. ": " .. f.coins .. " coins")
end

farm:describe()

This prints:

Sunflower Farm: 120 coins

farm:describe() is short for farm.describe(farm): Lua takes the function from the table and passes it the table itself as the first argument. Other arguments come after it: farm:earn(30) means farm.earn(farm, 30).

The colon works when you define a function, too. function farm:earn(amount) is short for function farm.earn(self, amount): Lua adds a hidden first parameter named self. When you call the function with a colon, self is the table you called it on:

local carrot = {name = "Carrot", age = 0, days = 3}

function carrot:grow()
    self.age = self.age + 1
end

function carrot:isReady()
    return self.age >= self.days
end

carrot:grow()
carrot:grow()
print(carrot.age, carrot:isReady())
carrot:grow()
print(carrot.age, carrot:isReady())

This prints:

2	false
3	true

In carrot:grow(), self is carrot, so self.age = self.age + 1 makes the carrot one day older. A function in an object that gets the object as self is called a method, and carrot:grow() is a method call. Inside a method, always use self, not the name of the table. Then the same method works for any table you call it on, and you'll need that in the next exercise.

Texts work the same way. In the lesson on strings, you learned that s:upper() is short for string.upper(s). Now you know what the colon does: s:upper() is s.upper(s), a method call with the text as the first argument:

local s = "carrot"
print(s:upper(), s.upper(s), string.upper(s))

This prints:

CARROT	CARROT	CARROT

How a text finds upper in the table string is something you'll see in the exercise on metatables.

The classic mistake: a dot instead of a colon. A method defined with a colon has to be called with a colon. With a dot, Lua doesn't pass the table, and self is nil:

local carrot = {name = "Carrot", age = 0, days = 3}

function carrot:grow()
    self.age = self.age + 1
end

carrot.grow()

The program ends with an error:

main.lua:4: attempt to index a nil value (local 'self')
stack traceback:
	main.lua:4: in field 'grow'
	main.lua:7: in main chunk

The first line points to line 4, inside the method, where self is used. But the mistake is in the line that called the method, the last one in the traceback: main.lua:7. in field 'grow' tells you that the function was called with a dot; a call with a colon would show in method 'grow'. Whenever an error mentions (local 'self'), look for a dot that should be a colon.

The opposite mistake is a function defined with a dot, function carrot.grow(days), and called with a colon, carrot:grow(2). The colon passes the table as the first argument, so days gets the carrot, and the 2 is ignored. The rule is simple: define methods with a colon, and call them with a colon.

Instructions

Turn both functions into methods: define them with a colon, function barn:report() and function barn:addEggs(count), use self inside them, and call them with a colon. The program prints the same as before.

Add a method barn:sellEggs(count) that takes count eggs out of the barn and returns how many coins they bring: 2 coins per egg. At the end of the program, sell 10 eggs, print the coins, and print the report again:

Barn: 12 eggs, 4 liters of milk
Barn: 18 eggs, 4 liters of milk
Sold 10 eggs for 20 coins
Barn: 8 eggs, 4 liters of milk

Start programming for free

By signing up, you agree to the Terms of Service and Privacy Policy.

Or sign up with:

2/9