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
type(obj)returns an object’s class/type.is_instance(obj, SomeType)checks whetherobjis an instance ofSomeType(or a subclass).
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)