Wiki

Clone wiki

Core / CodeaClasses

Back to Beyond the Codea in-app reference


FunctionIconOverview-Small.png Classes in Lua

Object-oriented programming (OOP) in Lua is the subject of Chapter 16 of Programming in Lua (1st Ed). The lua-users wiki also publishes a collection of links related to OOP in Lua.

Several object-oriented (OO) languages offer the concept of class. Lua does not have the concept of class. However, as discussed in Programming Lua (1st Ed), it is possible to emulate classes in Lua. Each object may have a 'prototype', which is an object where the first object looks up any operation that it does not know about. To represent a class, we create an object to be used exclusively as a prototype for other objects (its instances).

Classes in Codea

Codea comes with a built-in global function class() that is used to emulate classes.

Adding a class to a Codea project

For convenience, the '+' in the top right of the Codea editor offers the option of 'Create a New Class'. If chosen, you are prompted by the Editor to type the name of the new class. By default, class names start with a capital letter.

If you choose the class name 'MyClass', for example, the Editor creates a new tab named 'MyClass', with the following code as a template:

MyClass = class()

function MyClass:init(x)
    -- you can accept and set parameters here
    self.x = x
end

function MyClass:draw()
    -- Codea does not automatically call this method
end

function MyClass:touched(touch)
    -- Codea does not automatically call this method
end

In the example above, the statement MyClass = class() is executed before the setup() function is called. The class() function is built-in to Codea. Its Lua code is identified below.

For convenience, variables that are equal to class() are then included in the autocomplete functionality of Codea's editor.

Using Codea's class() function

The following example shows the creation of an 'Animal' base class and a 'Dog' class that inherits from 'Animal'.

In an 'Animal' tab:

----------------
-- Class: Animal 
----------------

Animal = class()

function Animal:init( word )
    self.talk = word
end

function Animal:speak()
    print( self.talk )
end

In a 'Dog' tab:

----------------
-- Class: Dog
----------------

Dog = class(Animal)

function Dog:init()
    Animal.init( self, "Woof!" )
end

In the 'Main' tab:

----------------
-- Main
----------------

function setup()
    -- Create a Dog
    d = Dog()
    d:speak()

    -- Create an Animal (not a Dog)
    b = Animal("Tweet!")
    b:speak()
end

The example above works because Animal has been created before Dog = class(Animal) is executed. Codea reads in each file (tab) in order and executes everything that is outside of a function ... end block.

If the example above is extended to include an 'Aardvark' class:

In an 'Aardvark' tab (which, by default, is placed alphabetically before the 'Animal' tab):

----------------
-- Class: Aardvark
----------------

Aardvark = class(Animal)

function Aardvark:init()
    Animal.init( self, "Yap! Yap!" )
end

Added to the setup() function in the 'Main' tab:

    -- Create an Aardvark
    a = Aardvark()
    a:speak()

When this code is run, it results in an error message in the Output panel like:

error: error: [string "..."]:9: attempt to call method 'speak' (a nil value)

That is because Animal has type nil when Aardvark = class(Animal) is executed.

If the 'Aardvark' tab is dragged after the 'Animal' tab, the code will run as expected.

Lua code for Codea's class() function

The Lua code for Codea's class() function is set out below and here:

-- Class.lua
-- Compatible with Lua 5.1 (not 5.0).

function class(base)
    local c = {}    -- a new class instance
    if type(base) == 'table' then
        -- our new class is a shallow copy of the base class!
        for i,v in pairs(base) do
            c[i] = v
        end
        c._base = base
    end

    -- the class will be the metatable for all its objects,
    -- and they will look up their methods in it.
    c.__index = c

    -- expose a constructor which can be called by <classname>(<args>)
    local mt = {}
    mt.__call = function(class_tbl, ...)
        local obj = {}
        setmetatable(obj,c)
        if class_tbl.init then
            class_tbl.init(obj,...)
        else 
            -- make sure that any stuff from the base class is initialized!
            if base and base.init then
                base.init(obj, ...)
            end
        end
        
        return obj
    end

    c.is_a = function(self, klass)
        local m = getmetatable(self)
        while m do 
            if m == klass then return true end
            m = m._base
        end
        return false
    end

    setmetatable(c, mt)
    return c
end

The function class has a single argument base and returns the table assigned to local variable c. If class is called as class(), without an argument, then base will be nil.

The table c has the following keys and values:

  • if base is a table, a copy of the keys and values in base and a key _base indexing base;
  • key __index indexing c itself; and
  • key is_a indexing a function. The function takes two arguments, self and klass, and returns true or false.

The table assigned to c has a metatable, being the table assigned to local variable mt.

The table mt has the following keys and values:

  • key __call indexing a function returning the table assigned to local variable obj. This function is called when a call is made on table c. The table assigned to obj has a metatable, being c itself. If c has key init, then that is called with obj as the first argument. Otherwise, if base is not nil and base has key init, then that is called with obj as the first argument.

Using the example of the 'Animal' class above, Animal = class() assigns the table returned by the class function to global variable Animal.

The block:

function Animal:init(word)
    self.talk = word
end

is equivalent to:

Animal["init"] = function (self, word) self.talk = word end

and the block:

function Animal:speak()
    print(self.talk)
end

is equivalent to:

Animal["speak"] = function (self) print(self.talk) end

The right-hand side of the assignment b = Animal("Tweet!") is a function call on the table assigned to Animal and __call comes into play. As Animal.init is not nil, Animal.init(obj, "Tweet!") is called and, ultimately, a table is returned, and assigned to b, with key talk indexing "Tweet!" and with a metatable with key __index set.

The statement b:speak() is equivalent to b["speak"](b). Now __index in the metatable of b comes into play, and b["speak"] returns the function Animal["speak"]. That function is called with argument b and, consequently, b.talk is printed.

Example projects using Codea's class() function

How the class() function can be used is illustrated in the following example projects provided with Codea 3D Lab, Physics Lab, Anagrams, Spritely, Bit Invader, Brick Out, Sounds Plus, Lua Jump, Dungeon Roller, and Ping.

Updated