Luce
Luce BaseSourceLuciaOS

10. Classes

10.1 Declaration and construction#

class Document:
    let title: str
    var dirty: bool = false
    var pages: list[Page] = []

    func init(self, title: str):
        self.title = title

    func append(self, page: Page):
        self.pages.append(page)
        self.dirty = true

let document = Document("Notes")

A class is a shared object: assignment and passing share it, and it lives while anything refers to it. Construction is Name(args), the same spelling as a struct; the declaration, not the use, says which is which. A class has one init(self, ...), which assigns every field without a default exactly once before it ends and cannot publish self before that; init may be ! when construction can fail. Name(args) then propagates in a fallible caller or is handled with catch. A class without an init and with defaults for every field is constructed with no arguments. Classes are final: no inheritance, no override, no base class. Alternative construction is a type function returning the class, Document.from_file(path).

10.2 Identity and mutation#

let first = Document("Draft")
let second = first
second.dirty = true
assert(first.dirty)
assert(first is second)

is and is not compare identity. == is not defined for a class unless it declares Equatable. A let field is assigned in init and never again; a var field is assigned through any binding of the object.

10.3 Lifetime#

Nothing in the language allocates, retains, releases or frees; the compiler and the runtime do. An object is destroyed when the last reference to it goes away, deterministically: a binding releases its reference when it leaves its scope or is reassigned, a field when it is reassigned or its owner is destroyed, and an object that no binding took, the result of a call or a construction inside an expression, at the end of the statement that produced it. A cycle of objects that nothing else refers to is reclaimed by the runtime's cycle collector, which runs at the end of the program and periodically before; docs/RUNTIME.md states the exact order. A class may declare deinit(self), run once at destruction, taking no arguments, returning unit, unable to fail, spawn, or publish self. Fields are then released in reverse declaration order. A class that holds a resource also offers close(), so that with can close it on time; deinit is the safety net.

10.4 Weak references#

Weak[T] is a library type holding a non-owning reference to a class instance: Weak(object) makes one and .get() yields T?, none once the object is gone. A Weak is a value: copying it copies the reference, and it has no display and no equality. It is for observer lists and caches. An ordinary back edge, a child's parent, may be a plain field; the collector handles the cycle.

10.5 Display#

Every scalar, str, bytes, tuple, struct and enum of displayable members, optional, and collection of displayable elements has a display used by print, str(x) and f-strings. A class or struct may declare its own by conforming to Display (§13.3). print(x) writes the display and a newline to standard output; print(a, b) separates with a space.