Basic Concepts

All LOOP class models present a set of common functionalities, as is presented in this section. For instance, all models are presented as sub-packages of the LOOP package. Therefore, to require a particular model you should use the following command:

-- use the LOOP model named 'simple'
-- for classes with simple inheritance
local oo = require "loop.simple"

Declaring Classes

All object models currently provided by LOOP are based on classes to implement behavioral sharing. Therefore, to create a LOOP object you must first define a class for it using the operation class(definition). This operation receives a table that must be used as the class. Generally, a LOOP class can be seen as the meta-table of all objects of that class, i.e. it contains the meta-methods that defines the behavior of its instances. Since object classes usually are also used to define common features provided by all instances, every LOOP class has a default implementation of the __index meta-method that retrieves fields from the class. This way, every field defined by the class is shared by all its instances. For example, suppose the following class definition.

-- model with no inheritance
local oo = require "loop.base"

local Date = oo.class{
  -- default field values
  day   = 1,
  month = 1,
  year  = 1900,
function Date:addyears(years)
  self.year = self.year + years
function Date:__tostring()
  return string.format("%d/%d/%d",

The class Date defines the fields day, month, year with default values and provides the implementation of the method addyear and the __tostring meta-method that is used by the tostring function of Lua base library. Therefore, all instances of class Date will share this behavior.

Creating Objects

Suppose the following code that creates an instance of class Date and uses it to find out the next birthday of a person.

function input(message, validator)
  return assert(validator(

local day   = input("Enter the day of your birth"  , tonumber)
local month = input("Enter the month of your birth", tonumber)
local year  = input("Enter the year of your birth" , tonumber)
local age   = input("Enter your age"               , tonumber)

-- new Date instance that overrides default values
local birthday = Date {
  day   = day,
  month = month,
  year  = year,
birthday:addyears(age + 1)
print("Your next birthday will be on", birthday)

All classes of LOOP models can be instantiated like in the example above that is using the class as a constructor that receives the values used to create an instance and returns it. Alternatively, instances can be created by the operation new that receives as parameters the class and the values used to create the instance.

Initializing Objects

The default way instances are created is using the first argument informed as the object that must become an instance of the class. That is why the above example passes a table for the class in order to create the instance. However, this behavior can be redefined by the definition of the __init field on the class. This field works like a meta-method that is used to create instances of a class. The function stored at field __init receives the class and all the values provided to the class constructor. For example, suppose that we want to change the class Date so it can be instantiated from a tripe of numbers instead of a table containing possible replacements of default attribute values, then we can use the code provided below.

function Date:__init(month, day, year)
  -- self is the class
  return oo.rawnew(self, {
    day   = day,
    month = month,
    year  = year,

The rawnew function is used to forcefully turn a table into an instance of a class without calling the __init function. The rawnew function can be roughly comparable to the setmetatable function of Lua with switched parameters (actually that is its implementation in most LOOP models). The main purpose of the __init function is to initialize a table so it can be turned into an instance of a class. This way the class may define may different semantics for the constructor. For example, consider the two following types of constructors.

function Constructed:__init(...)
  -- creates and initialize a brand
  -- new instance with the values of
  -- provided parameters
  return oo.rawnew(self, { ... })

function Mutant:__init(object)
  object = object or {}
  -- optionally checks if the object
  -- presents the expected state
  -- and turns it into an instance
  return oo.rawnew(self, object)

When Constructed constructor is called, it always return a brand new instance, so it is not possible to properly make an existing table an instance of Constructed (unless using the rawnew function). On the other hand, the constructor of Mutant class can be used to initialize the table properly prior to the actual instantiation performed by rawnew.

Introspection Mechanisms

Finally, all LOOP models provide introspection operations like classof(object), isclass(table), instanceof(object, class), memberof(class, name) and members(class) for retrieving the LOOP class of an object, check whether a table is a class of the LOOP model, check whether an object is an instance of a given class, get the value of a member defined in a class, and iterating through all class members, respectively.

Copyright (C) 2004-2008 Tecgraf, PUC-Rio

This project is currently being maintained by Tecgraf at PUC-Rio.