Class Models for Lua

# 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
    end
    function Date:__tostring()
      return string.format("%d/%d/%d",
                           self.month,
                           self.day,
                           self.year)
    end

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)
      print(message)
      return assert(validator(io.read()))
    end

    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,
      })
    end

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, { ... })
    end

    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)
    end

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.