Module Reference

loop.proto

This module is intended to support prototype-based programming. It is basically provides functions to clone objects and inspect them. All members not defined in the cloned objects themselves are inherited dynamically from the respective prototype. Therefore, if the prototype changes, these changes are reflected in its clones.

Functions

clone(proto [, clone])
Returns a clone of object proto. The metatable of the returned object shall not be modified as it is used internally by the module implementation.
getproto(object)
Returns the prototype of clone object. If object is not a clone (i.e. a usual table) then nil is returned.
iscloneof(clone, proto)
Returns true if clone is a clone of proto or false otherwise.

Example

StackProto = {}
function StackProto:empty()
  return #self == 0
end
function StackProto:push(item)
  self[#self+1] = item
end
function StackProto:pop()
  if not self:empty() then
    local top = #self
    local item = self[top]
    self[top] = nil
    return item
  end
end


local oo = require "loop.proto"
s = oo.clone(StackProto)
s:push("Bottom")
s:push("Middle")
s:push("Top")
print(s:pop())   --> Top
print(s:pop())   --> Middle
print(s:pop())   --> Bottom
print(s:empty()) --> true

loop.base

This is the most basic of the class-based LOOP modules. The base module is ideal for definition of classes without super-classes.

Functions

class([table])
Returns a class. The optional parameter table defines a table that shall become the class. If this parameter is not provided a new table is created to represent the class. For more information about LOOP classes see this.
getclass(object)
Returns the class of object. If object is not a LOOP class instance then its meta-table is returned.
isinstanceof(object, class)
Returns true if object is an instance of class or false otherwise.
isclass(table)
Returns true if table is a class or false otherwise.
getmember(class, name)
Returns the value of member indexed by name defined in class class. Inherited members are ignored by this function.
members(class)
Returns an iterator that may be used in a for statement to iterate through all the members defined by the class. The iteration variables hold the field name and value respectively.
new(class, ...)
Returns an instance of class constructed accordingly to the values of the extra arguments.
rawnew(class [, object])
Makes object an instance of class without calling the __new metamethod of the class. If no object is provided, a new table is created to represent the new instance.

Example

Queue = oo.class{
  head = 1,
  tail = 1,
}
function Queue:empty()
  return self.head >= self.tail
end
function Queue:enqueue(item)
  local tail = self.tail
  self[tail] = item
  self.tail = tail+1
  return item
end
function Queue:dequeue()
  if not self:empty() then
    local head = self.head
    local item = self[head]
    self[head] = nil
    self.head = head+1
    return item
  end
end


q = Queue()
q:enqueue("First")
q:enqueue("Second")
q:enqueue("Last")
print(q:dequeue()) --> First
print(q:dequeue()) --> Second
print(q:dequeue()) --> Last
print(q:empty())   --> true

loop.simple

This is another class-based module and it adds the possibility of defining classes with simple inheritance. The class function of the simple module takes an optional second argument that defines the super-class of the class being created. Additionally, the simple module introduce the functions getsuper(class) to retrieve the super-class of a given class and issubclassof(class, super) to check whether a class is sub-class of other.

Functions

All from loop.base module and (re)defines:

class([table [, super]])
Returns a class. The optional parameter table defines a table that shall become the class. If this parameter is not provided a new table is created to represent the class. The optional parameter super defines the superclass of the returned class. If this second parameter is not provided this function behaves as the class function from the loop.base module.
getsuper(class)
Returns the super-class of class. If class is not a class of the module or does not define a super class then it returns nil.
issubclassof(class, super)
Returns true if class is a sub-class of super or false otherwise.

Example

Circle = oo.class()
function Circle:diameter()
  return self.radius * 2
end
function Circle:circumference()
  return self:diameter() * 3.14159
end
function Circle:area()
  return self.radius * self.radius * 3.14159
end

Sphere = oo.class({}, Circle)
function Sphere:area()
  return 4 * self.radius * self.radius * 3.14159
end
function Sphere:volume()
  return 4 * 3.14159 * self.radius^3 / 3
end

function show(shape)
  print("Shape Characteristics")
  print("  Side:         ", shape.radius)
  print("  Diameter:     ", shape:diameter())
  print("  Circumference:", shape:circumference())
  print("  Area:         ", shape:area())
  if shape.volume ~= nil then
    print("  Volume:        ", shape:volume())
  end
end

c = Circle{ radius = 20.25 }
s = Sphere{ radius = 20.25 }

show(c)
show(s)

loop.multiple

This module enables the definition of classes with multiple inheritance. The class function of the multiple module takes a sequence of optional arguments that defines the set of super-classes of the class being defined. The order of the super-classes provided defines the priority of field inheritance therefore the value of a inherited field is defined by the leftmost class that provides such field. The multiple module introduce the new function supers(class) that returns an iterator used to iterate through the list of direct super-classes of a class. Additionally, the getsuper(class) function is changed so it returns all the super-classes of a given class.

Functions

All from loop.simple module and (re)defines:

class(table, ...)
Returns a class. The optional parameter table defines a table that shall become the class. If this parameter is not provided a new table is created to represent the class. The additional parameters ... defines all the superclasses of the returned class. If less than two superclasses are provided this function behaves as the class function from the loop.simple module.
getsuper(class)
Returns all the super-classes of class. If class does not define a super-class it returns nil.
supers(class)
Returns an iterator that may be used in a for statement to interate over all super-classes of the class class.

Example

Contained = oo.class{}
function Contained:__new(object)
  assert(object, "no object supplied")
  assert(object.name, "no name for object")
  assert(object.container, "no container for object")
  object.container:add(object.name, object)
  return oo.rawnew(self, object)
end


Container = oo.class{}
function Container:__new(object)
  object = object or {}
  object.members = object.members or {}
  return oo.rawnew(self, object)
end
function Container:add(name, object)
  self.members[name] = object
end
function Container:search(path)
  local container, newpath = string.match(path, "(.-)/(.+)$")
  if container then
    container = self.members[container]
    if container and container.search then
      return container:search(newpath)
    end
  else
    return self.members[path]
  end
end


ContainedContainer = oo.class({}, Contained, Container)
function ContainedContainer:__new(object)
  object = Contained.__new(self, object)
  object = Container.__new(self, object)
  return object
end


Root = Container{}
Folder = ContainedContainer{
  container = Root,
  name = "my_folder",
}
File = Contained{
  container = Folder,
  name = "my_file.txt",
  data = "Hello, I'm a file"
}
print(Root:search("my_folder/my_file.txt").data) --> Hello, I'm a file

loop.cached

Classes created with modules loop.simple and loop.multiple behave like clones of their superclasses in the sense they always consult the superclasses to retrieve fields they don't have. In order to avoid the search through the complete hierarchy of classes every time a class field is indexed, LOOP provides the cached module. In this module, classes copy the fields defined by their super-classes to themselves (i.e. meta-table). This cache of inherited fields makes instances of classes with simple or multiple inheritance as efficient as classes of the loop.base module. Other advantage of the cache module is that meta-methods like the __index can be shared across the hierarchy of classes because they are copied to each class (i.e. meta-table). Currently, Lua ignores the __index when accessging the fields of meta-tables (see the Lua manual).

On the other hand, to properly update the cache of inherited fields whenever a class is changed, the classes of this module are manipulated through proxies. These proxies intercept any changes and update all caches of inherited field through out the entire class hierarchy. This indirection makes class operation more expensive than in other modules where classes are simple meta-tables. All functions of the cached module manipulates proxies of actual classes. The cached module introduces the new function allmembers that return an iterator for all members provided by a class, including the inherited ones.

Functions

All from loop.multiple module and (re)defines:

allmembers(class)
Returns an iterator that may be used in a for statement to interate over all members provided by the class class, including the inherited ones. The iteration variables hold the field name and value respectively.

Example

CachedObject = oo.class()

function CachedObject:__index(field)
  local value = oo.classof(self)[field]
  if value then rawset(self, field, value) end
  return value
end

CachedSphere = oo.class({}, CachedObject, Sphere)

s = CachedSphere{ radius = 2 }
show(s)

print("Object fields:")
for field, value in pairs(s) do
  print("", field, value)
end

loop.scoped

This module provides features to define classes with private and protected access scopes. Each class of scoped module can provide a definition of a private behavior that will be perceived only by methods defined in that class and a protected behavior that will be perceived only by the methods of the object, i.e. will not be seen by functions not defined by some class inherited by the object. The private and protected behaviors are defined by fields private and protected that must contain tables defining the fields presented by the private or protected scope of the instances of that class. All the other fields are publicly available.

The fields publicly available are also available in the private and the protected scopes. Similarly, the fields available in the protected scope are also visible in the private scope. The scope management is done by replacing the self reference in the calls of instance methods. Therefore, in every call of a method the self is replaced by the proper scoped state object. The mapping of the private and protected scoped state of each object instance is automatically made by the scoped module. Additionally, the creation of the scoped states is done on demand, so such states are only created at invocation of methods defined in classes that defines private or protected states.

The scoped module was mainly devised for applications with objects written in Lua that must protect some internal state from unexpected accesses, like bad user script code or third-party interacting components that may not know or care about the internal object implementation. Unfortunately, the management of such scoped states is extremely expensive both in terms of memory and processing time when compared to other modules. Therefore, the scoped module should only be used in applications that actually need such infrastructure.

Even though it is a hard task to provide arbitrary private and protected state in a programming model similar to the one used when applying an object-oriented style to Lua, there are many alternatives to implement objects with private state in Lua. Such alternatives include the use of function closures as objects with private state stored in upvalues. On the other hand, the scoped module may be used for prototyping and experimental applications used as proof of concept.

Functions

All from loop.cached module and (re)defines:

priv(object [, class])
Returns the private state of object relative to class class. This object can be any self of a scoped object, i.e. the private, procetected or even public state. If no class is provided then the actual object class is used.
prot(object)
Returns the protected state of object. This object can be any self of a scoped object, i.e. the private, procetected or even public state.
this(object)
Returns the public referece of object. This object can be any self of a scoped object, i.e. the private, procetected or even public state.

Example

SQLTable = oo.class{
  private = {
    SQLTemplate = [[
      SELECT *
      FROM %s
      WHERE %s = '%s'
    ]],
  },
}
function SQLTable:__new(database, tablename, keyfield)
  self = oo.rawnew(self)
  rawset(oo.priv(self), "db", database)
  rawset(oo.priv(self), "table", tablename)
  rawset(oo.priv(self), "key", keyfield)
  return self
end
function SQLTable:__index(keyvalue)
  local sql = self.SQLTemplate:format(self.table, self.key, keyvalue)
  return self.db:query(sql)[1]
end

People = SQLTable(MySQLDB, "People", "Name")

print(People["John Doe"].Age)

loop.classops

This module provides generic functions for manipulation of classes that follow a basic model adopted by all LOOP modules as described here. This module is only required when you manipulate classes created wiht different LOOP modules in a single application. This module does not provide a class function to create classes. Use other modules to create classes.

Functions

All from loop.multiple module, except function class.

Example

base = require "loop.base"
simple = require "loop.simple"
multiple = require "loop.multiple"
cached = require "loop.cached"

A = base.class()
B = simple.class()
C = multiple.class()
D = cached.class()

oo = require "loop.classops"

print(oo.isclass(A)) --> true
print(oo.isclass(B)) --> true
print(oo.isclass(C)) --> true
print(oo.isclass(D)) --> true

loop.hierarchy

This module provides functions to implement initialization mechanisms of instances of complex class hierarchies (like Java or C++ constructors) using the infrastructure provided by the __new metamethod. For more information on different initialization approaches see section Hierarchical Initialization.

Functions

creator(class [, ...])
Creates an empty instance of class class and calls method __init of this instance with the given arguments and return this new instance.
mutator(class [, object [, ...]])
Turns the given table object into an instance of class class and returns it. It also calls method __init defined in each of the classes of the returned object.
topdown(class)
Returns an iterator function to be used in a for to iterate over all the class hierarchy of class in top-down order.

Example



loop.scoped.debug

This module provides functions to inspect methods of classes created with the loop.scoped module. The methods of such classes has references to the class they are defined that can be inspected using the The Debug Library.

Functions

methodfunction(method)
Returns the function that implements the method method.
methodclass(method)
Return the class where method method was defined.

Example

table = require "table"
oo = require "loop.scoped"
ood = require "loop.scoped.debug"

Array = oo.class{
  insert = table.insert,
  remove = table.remove,
}

a = Array{ 1,2,3 }
assert(a.insert ~= table.insert)
assert(ood.methodfunction(a.insert) == table.insert)
assert(ood.methodclass(a.insert) == Array)

loop.table

This module provides functions to copy table fields, clear table fields and also a function to create memoize tables.

Functions

copy(source [, destiny])
Copies all pairs stored in table source to table destiny and returns the later. If destiny is nil a new empty table is used instead.
clear(table)
Removes all pairs stored in table table and returns this table.
memoize(func [, weak])
Returns a memoize table for function func. Every time a new value is used to index the memoize table the function func is invoked with the indexed value as parameter. If the result of the invocation produces a value different from nil this value is stored in the memoize table, so the next time the same value is indexed the stored value is produced without calling function func again. Parameter mode indicates the weakness of the memoize table, as defined for field __mode of metatables.

Example

table = require "loop.table"

chunkOf = table.memoize(loadstring, "v")

f = chunkOf[ 'print "Hello, World!"' ]
f() --> Hello, World!

Copyright (C) 2004-2018 Renato Maia

This project was originally developed in Tecgraf at PUC-Rio.