Constructors

The carrot from the previous exercise is one table, written out by hand. But a farm has many crops: carrots, potatoes, pumpkins… Writing out each one with all its methods would mean the same code again and again. Instead, write a function that makes them. A function that creates a new object and returns it is called a constructor:

local function newCrop(name, days)
    local crop = {name = name, days = days, age = 0}

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

    function crop:describe()
        print(self.name .. ": day " .. self.age .. " of " .. self.days)
    end

    return crop
end

local carrot = newCrop("Carrot", 3)
local pumpkin = newCrop("Pumpkin", 7)
carrot:grow()
carrot:grow()
pumpkin:grow()
carrot:describe()
pumpkin:describe()

This prints:

Carrot: day 2 of 3
Pumpkin: day 1 of 7

Every call of newCrop runs its code again: it creates a new table, puts the two methods into it, and returns it. carrot and pumpkin are two separate tables, so when the carrot grows, the pumpkin doesn't change. And because the methods use self, not a fixed name, the same code works for both.

In {name = name, days = days, age = 0}, the same word appears twice, which is usual in constructors. On the left of = is the key, on the right is the value, here the parameter of the same name. So newCrop("Carrot", 3) creates {name = "Carrot", days = 3, age = 0}.

The classic mistake: forgetting return. Without return crop, the constructor creates the table but doesn't give it back, so the variable gets nil:

local function newCrop(name, days)
    local crop = {name = name, days = days, age = 0}

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

local carrot = newCrop("Carrot", 3)
carrot:grow()

The program ends with an error:

main.lua:10: attempt to index a nil value (local 'carrot')

The error points to the line where you first use the object, not to the constructor. When a new object is nil, check the constructor's return.

One problem. Each call of newCrop also creates the methods again: the lines with function crop:grow() run again and make a new function. The carrot's grow and the pumpkin's grow do the same thing, but they're two different functions:

local function newCrop(name)
    local crop = {name = name, age = 0}

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

    return crop
end

local carrot = newCrop("Carrot")
local pumpkin = newCrop("Pumpkin")
print(carrot.grow == pumpkin.grow)

This prints:

false

For three crops, that doesn't matter. But a field of 1,000 crops with ten methods each would hold 10,000 functions that are copies of the same ten. It would be better to keep the methods in one place, shared by all crops, and let each crop hold only its own data: its name, days and age. That's what metatables are for, and they're the topic of the next exercise.

Instructions

Emma has more hens than just Goldie. Turn the code that creates Goldie into a constructor: a local function newHen(name) that creates a hen with the given name and 0 eggs, gives it both methods, and returns it. Then create Goldie with local goldie = newHen("Goldie"). The program prints the same as before.

Below Goldie, use newHen to create a second hen, Pearl. Pearl lays 3 eggs, and then 1 more. Print her report below Goldie's:

Goldie laid 2 eggs
Pearl laid 4 eggs

Check what this exercise says about methods: at the end of the program, print goldie.lay == pearl.lay. The program prints:

Goldie laid 2 eggs
Pearl laid 4 eggs
false

Start programming for free

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

Or sign up with:

3/9