lua_oop/README.md

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:

  • Int
  • Float
  • Str
  • Bool
  • Tuple

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)
  • Queue
  • Stack
  • Dict
  • Set

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, but Class(...) is preferred.