MALDA™ Reference Manual

The AI-First Programming Language - Version 1.0.39

11. Classes & Objects

A class is a named type with fields (state) and methods (behavior). new ClassName(...) runs the constructor and returns an instance. Same file as the course lesson: Examples/Basics/classes_intro.malda. Fuller listing: Examples/OOP/classes_objects.malda.

11.1 A working class

The constructor has the same name as the class. Inside a method you can read a field as name; write this.name when a parameter or local shadows the field (as the constructor does). A same-named binding in the enclosing module does not shadow the field. Methods are functions declared inside the class.

class Person {
    public var name;
    public var age;

    function Person(name, age) {
        this.name = name;
        this.age = age;
    }

    public function introduce() {
        io.print("Hi, I'm " + name + " and I'm " + string(age) + " years old.");
    }

    public function getAge() {
        return age;
    }

    public function setAge(newAge) {
        age = newAge;
    }
}

var person1 = new Person("Alice", 30);
var person2 = new Person("Bob", 25);
person1.introduce();
person2.introduce();
io.print("Alice's age: " + string(person1.getAge()));
person1.setAge(31);
io.print("Alice's new age: " + string(person1.getAge()));
Do not call new Person(...) unless class Person is declared in the same program (or imported). A snippet that only shows new Person is not a complete example.
First-read stop. Inheritance and the rest of this chapter are below. The course lesson is Learn MALDA — Lesson 12.

11.2 Access modifiers

11.3 Inheritance

A class can extend one parent with extends. Call super(...) from the child constructor, and super.method() to reuse a parent method. Same file: Examples/OOP/inheritance.malda.

class Animal {
    public var name;

    function Animal(name) {
        this.name = name;
    }

    public function speak() {
        print(name + " makes a sound");
    }
}

class Dog extends Animal {
    function Dog(name) {
        super(name);
    }

    public function speak() {
        print(name + " says: Woof!");
    }
}

class Cat extends Animal {
    function Cat(name) {
        super(name);
    }

    public function speak() {
        print(name + " says: Meow!");
    }
}

var dog = new Dog("Rex");
var cat = new Cat("Fluffy");
dog.speak();
cat.speak();

To run the parent implementation as well, call super.speak() inside the override before or after the child body.

11.4 Static members

Static members belong to the class, not instances. Inside the class's own methods, a static field is also reachable by its bare name (like an implicit this. for instance fields):

class MathUtils {
    static var PI = 3.14159;

    static function add(a, b) {
        return a + b;
    }
}

var pi = MathUtils.PI;
var sum = MathUtils.add(5, 3);
class Counter {
    static var count = 0;

    static function increment() {
        count = count + 1; // bare name refers to the static field
    }

    static function getCount() {
        return Counter.count; // qualified access also works
    }
}

Counter.increment();
Counter.increment();
print(Counter.getCount());

11.5 Operator overloading

Classes can overload selected operators by defining methods with reserved names. For binary operators, MALDA first tries the left operand overload (__add__, __sub__, etc.). If that overload is not found, MALDA then tries the right operand reversed overload (__radd__, __rsub__, etc.) with the left operand passed as argument. For unary operators, the single operand is the receiver.

OperatorLeft-hand methodRight-hand fallback method
+__add__(other)__radd__(other)
- (binary)__sub__(other)__rsub__(other)
*__mul__(other)__rmul__(other)
/__div__(other)__rdiv__(other)
%__mod__(other)__rmod__(other)
==__eq__(other)__req__(other)
!=__neq__(other)__rneq__(other)
<__lt__(other)__rlt__(other)
<=__le__(other)__rle__(other)
>__gt__(other)__rgt__(other)
>=__ge__(other)__rge__(other)
- (unary)__neg__()n/a

If no matching left-hand or right-hand overload method exists, MALDA uses the built-in operator behavior.

class Value {
    public var data;

    function Value(data) {
        this.data = data;
    }

    public function __add__(other) {
        return data + other.data;
    }
}

var a = new Value(2);
var b = new Value(3);
print(a + b); // 5
class RightValue {
    public var data;

    function RightValue(data) {
        this.data = data;
    }

    public function __radd__(other) {
        return other + data;
    }
}

var rhs = new RightValue(7);
print(5 + rhs); // 12

11.6 Primary constructors

A parameter list after the class name is a primary constructor. Each parameter becomes a public field of the same name, and MALDA synthesizes a constructor that assigns those fields. The optional body is a normal class body (methods, extra fields, static members, operator overloads). There is no extra keyword: class Name( is distinct from class Name { and from class Name extends.

Those forms desugar to a classic class: public fields, a constructor with the same name as the class that assigns this.x = x (and so on), then any members from the body. Optional type hints on parameters are stored on those fields: class Point(x: float, y: float).

class Point(x, y);
var p = new Point(3, 4);
print(p.x + p.y);
class Point(x, y) {
    function total() {
        return this.x + this.y;
    }
}
print(new Point(3, 4).total());
var x = 10;
class Point(x, y) {
    function total() {
        return x + y;
    }
}
print(new Point(2, 3).total());
class Point(x, y) function total() this.x + this.y;
print(new Point(3, 4).total());

11.7 Adding methods to an already-defined class

A later class Name { … } with the same name augments the original class: new members are appended. There is no extra keyword. A later declaration cannot use a primary constructor, cannot change extends, and cannot repeat a member name that is already present. The same merge applies across REPL entries in one session (each entry is parsed on its own; the interpreter appends members to the class already bound). Instances created before the later entry see the new methods.

Methods share the compact function body: a single expression plus ; is return of that expression. For one simple method you can also omit the class braces:

class Point(x, y);
class Point { function total() this.x + this.y; }
print(new Point(3, 4).total());
class Point(x, y);
class Point function doubled() this.x * 2;
print(new Point(3, 4).doubled());
class Point(x, y);
class Point {
    function total() {
        return this.x + this.y;
    }
}
print(new Point(3, 4).total());
class Counter {
    var value = 0;

    function Counter() {
    }
}

class Counter {
    function inc() {
        this.value = this.value + 1;
        return this.value;
    }
}

var c = new Counter();
print(c.inc());
print(c.inc());

See Also