Skip to the content.

Classes and objects

Chloride has “a proper class and inheritance system” (per the project README) — classes are real objects, supporting inheritance and introspection.

Defining a class

class name do
    this.__init__(self) = do
        term.log("hello world")

term.log(name)      # the class itself
term.log(name())    # calling it constructs an instance; prints "hello world"

(from class_test.ar)

Inside a class body, this.method_name(self, ...) = do ... defines an instance method. The first parameter is conventionally named self and refers to the instance, similar to Python.

Inheritance

Pass the parent class in parentheses after the class name:

class test(name) do
    this.wow(self) = do
        term.log(self)

test().wow()

(from class_test.ar) — test inherits from name.

Public vs. private members

Members defined with this.member = ... are public — accessible from outside the class. Members defined with a plain let inside the class body are private — only accessible from within the class’s own methods:

class some_public_private_statics do
    this.some_public = "hello im public"
    let some_private = "hello im private"

    this.get_the_private(self) = do
        return some_private

term.log(some_public_private_statics.some_public)   # OK — public
term.log(some_public_private_statics.some_private)  # errors — private, not accessible

(from class_test.ar, which explicitly comments that accessing the private member from outside # will error)

Static vs. instance members

this.some_public in the example above is a static/class-level member (accessed on the class itself, some_public_private_statics.some_public, without needing an instance). Instance data is normally set inside __init__ via self.attr = value:

class custom_range do
    this.__init__(self, stop) = do
        self.stop = stop
    this.__iter__(self) = custom_range_iterator(self.stop)

(from custom_range.ar)

The constructor: __init__

__init__ runs when the class is called like a function:

class something do
    this.__init__(self, x) = do
        self.x = x
    this.f(self) = do
        term.log(self, self.x, self.x = self.x + 1)

let instance = something(0)
instance.f()   # logs the instance, its x, and increments x

(from import_test.ar, showing that an assignment like self.x = self.x + 1 is itself an expression that returns the assigned value — this is why it can appear as a term.log(...) argument.)

Operator overloading (dunder methods)

Classes can customise how they behave with built-in operators and syntax by implementing “dunder” (double-underscore) methods. These are exactly how the built-in types (number, string, array, etc.) implement their own behaviour, so you can make your own types work just as naturally.

Method Triggered by
__init__(self, ...) calling the class, e.g. MyClass(args)
__string__(self) converting to a string / term.log
__getitem__(self, key) obj[key]
__setitem__(self, key, value) obj[key] = value
__contains__(self, item) item in obj
__iter__(self) starting a for loop over obj
__next__(self) each step of a for loop, on an iterator object
__equal__ / __not_equal__ == / !=
__less_than__ / __less_than_equal__ / __greater_than__ / __greater_than_equal__ <, <=, >, >=
__add__ / __subtract__ / __multiply__ / __division__ / __floor_division__ / __modulo__ / __exponent__ +, -, *, /, //, %, **
__negation__ unary -
__hash__ using the object as a dictionary key
__name__ a class-level display name (used by exception types, for example)

Example: custom subscript / slice handling

class x do
    this.__getitem__(self, key) = key
    this.__setitem__(self, key, value) = key

term.log(x()[1:2, 2])
term.log(x()[1:2, 2] = 1)

(from slice.ar)

Example: making a class iterable

class custom_range_iterator do
    this.__init__(self, stop) = do
        self.current = 0
        self.stop = stop
    this.__next__(self) = do
        if (self.stop <= self.current) do
            throw StopIteration()
        let result = self.current
        self.current = self.current + 1
        return result

class custom_range do
    this.__init__(self, stop) = do
        self.stop = stop
    this.__iter__(self) = custom_range_iterator(self.stop)

(from custom_range.ar) — see for loops and iterators for how this plugs into for.

Introspection

term.log(type(5))                 # number
term.log(is_instance(5, number))  # true

You can also assign to class-level attributes dynamically through type():

term.log(type(some_public_private_statics_instance).some_public = 10)

(from class_test.ar)

Next