3.9 KiB
Lua OOP + Types Tutorial
This project provides a small OOP layer with Python-like ergonomics and a set of Python-inspired types.
Contents
- OOP: class definitions as functions, inheritance, visibility, auto-bound methods
- Types: Int, Float, Str, Bool, Tuple, List (linked), Queue, Stack, Dict, Set
OOP quick start
The OOP system lets you define a class as a function that receives cls. Instances are created by calling the class itself.
oop = require("oop/oop")
function Animal(cls)
function cls.__init(this, name)
this.name = name
end
function cls.speak(this)
print(this.name .. " makes a noise.")
end
end
function Dog(cls)
cls.inherit(Animal)
function cls.speak(this)
cls.super.speak(this)
print(this.name .. " barks!")
end
end
local d = Dog("Milo")
d.speak()
How instance calls work
Methods are defined with dot syntax and explicit this:
function cls.move(this, dx, dy)
this.x = this.x + dx
this.y = this.y + dy
end
Calls also use dot syntax. this is auto-passed for you:
obj.move(1, 2)
Do not use : calls with this system because methods are already auto-bound.
Visibility (public / protected / private)
Visibility is checked when reading or writing members. Use cls.visibility or Visibility:
function User(cls)
cls.setattr(cls, "name", nil, cls.visibility.PUBLIC)
cls.setattr(cls, "password", nil, cls.visibility.PRIVATE)
cls.method("set_password", function(this, pw)
this.password = pw
end, cls.visibility.PRIVATE)
function cls.__init(this, name, pw)
this.name = name
this.password = pw
end
end
- public: accessible everywhere
- protected: accessible in the class and subclasses
- private: accessible only inside the class itself
Errors show where the access was attempted and from which class.
Inheritance
Use cls.inherit(ParentClass):
function Base(cls)
function cls.hello(this)
print("base")
end
end
function Sub(cls)
cls.inherit(Base)
function cls.hello(this)
cls.super.hello(this)
print("sub")
end
end
cls.super exposes the direct parent only.
Types module
All types live in oop/types.lua:
local Types = require("oop/types")
Primitive types (immutable)
Primitives are immutable after construction:
IntFloatStrBoolTuple
You can call a primitive to get its value, and use .copy() to create a new one.
local i = Types.Int(42)
print(i()) -- 42
print(i.copy()) -- new Int
Tuple stores items immutably:
local t = Types.Tuple({ 1, 2, 3 })
print(t()) -- immutable items proxy
print(t.get(2)) -- 2
Mutable types
List(linked list)QueueStackDictSet
Examples:
local l = Types.List({ "a", "b" })
l.append("c")
print(l, #l, l.get(2))
local q = Types.Queue({ 1, 2 })
q.enqueue(3)
print(q.dequeue(), q.peek())
local st = Types.Stack({ 9, 8 })
st.push(7)
print(st.pop(), st.peek())
local d = Types.Dict({ one = 1, two = 2 })
print(d.get("two"))
d.set("three", 3)
print(#d)
local s = Types.Set({ "x", "y" })
s.add("z")
print(s.has("x"), #s)
Dunder methods (operators)
Classes support common Lua metamethods. If you do not override them, they raise an error.
Example vector:
function Vec2(cls)
function cls.__init(this, x, y)
this.x = x
this.y = y
end
function cls.__add(a, b)
return Vec2(a.x + b.x, a.y + b.y)
end
function cls.__tostring(this)
return "Vec2(" .. this.x .. ", " .. this.y .. ")"
end
end
local v = Vec2(1, 2) + Vec2(3, 4)
print(v)
Notes and tips
- Class functions with capitalized names are auto-wrapped into classes when assigned to globals.
- Methods must be called with dot syntax, not colon syntax.
new(Class, ...)still works, butClass(...)is preferred.