ESC
Type to search...
S
Soli Docs

Classes & OOP

Object-oriented programming in Soli: classes, inheritance, interfaces, and static members.

Basic Class Definition

class

Defines a class with properties and methods.

class Person {
    name: String;
    age: Int;
    email: String;

    new(name: String, age: Int, email: String = null) {
        this.name = name;
        this.age = age;
        this.email = email ?? "";
    }

    fn greet() -> String {
        return "Hello, I'm " + this.name;
    }

    fn introduce() -> String {
        let intro = "Hi, I'm " + this.name + " and I'm " + str(this.age) + " years old";
        if (this.email != "") {
            intro = intro + ". You can reach me at " + this.email;
        }
        return intro;
    }

    fn have_birthday() {
        this.age = this.age + 1;
    }
}

// Creating instances
let alice = new Person("Alice", 30);
let bob = new Person("Bob", 25, "[email protected]");

// Using instances
print(alice.greet());      // "Hello, I'm Alice"
print(bob.introduce());    // "Hi, I'm Bob and I'm 25 years old..."

alice.have_birthday();
print(alice.age);          // 31

Inheritance

extends

Create a subclass that inherits from a parent class.

// Base class
class Animal {
    name: String;
    age: Int;

    new(name: String, age: Int) {
        this.name = name;
        this.age = age;
    }

    fn speak() -> String {
        return this.name + " makes a sound";
    }

    fn get_info() -> String {
        return this.name + " is " + str(this.age) + " years old";
    }
}

// Subclass
class Dog extends Animal {
    breed: String;

    new(name: String, age: Int, breed: String) {
        super(name, age);
        this.breed = breed;
    }

    // Override method
    fn speak() -> String {
        return this.name + " barks!";
    }

    // Subclass-specific method
    fn fetch() -> String {
        return this.name + " fetches the ball!";
    }
}

// Using inheritance
let dog = new Dog("Buddy", 3, "Golden Retriever");
print(dog.speak());        // "Buddy barks!"
print(dog.get_info());     // "Buddy is 3 years old"
print(dog.fetch());        // "Buddy fetches the ball!"
print(dog.breed);          // "Golden Retriever"

Interfaces

interface / implements

Define contracts that classes must implement.

// Define an interface
interface Drawable {
    fn draw() -> String;
    fn get_color() -> String;
}

// Another interface
interface Resizable {
    fn resize(width: Float, height: Float);
    fn get_dimensions() -> {width: Float, height: Float};
}

// Class implementing multiple interfaces
class Circle implements Drawable, Resizable {
    radius: Float;
    color: String;

    new(radius: Float, color: String) {
        this.radius = radius;
        this.color = color;
    }

    fn draw() -> String {
        return "Circle with radius " + str(this.radius) + " and color " + this.color;
    }

    fn get_color() -> String {
        return this.color;
    }

    fn resize(width: Float, height: Float) {
        this.radius = width / 2;
    }

    fn get_dimensions() -> {width: Float, height: Float} {
        return {"width": this.radius * 2, "height": this.radius * 2};
    }
}

// Using interfaces
let shapes: Drawable[] = [
    new Circle(5.0, "red"),
];

for (shape in shapes) {
    print(shape.draw());  // Polymorphic call
}

Visibility Modifiers

Modifier Access
public Accessible from anywhere (default)
private Intended for use only within the class
protected Intended for use within class and subclasses

Note: Visibility modifiers are currently parsed for documentation purposes but not enforced at runtime.

class BankAccount {
    public account_number: String;
    private balance: Float;

    new(account_number: String, initial_deposit: Float) {
        this.account_number = account_number;
        this.balance = initial_deposit;
    }

    public fn deposit(amount: Float) -> Bool {
        if (this.validate_amount(amount)) {
            this.balance = this.balance + amount;
            return true;
        }
        return false;
    }

    public fn get_balance() -> Float {
        return this.balance;
    }

    private fn validate_amount(amount: Float) -> Bool {
        return amount > 0;
    }
}

let account = new BankAccount("123456789", 1000.0);
account.deposit(500.0);           // Works - public method
print(account.get_balance());     // 1500.0

Static Members

static

Properties and methods that belong to the class, not instances.

class MathUtils {
    static PI: Float = 3.14159265359;
    static E: Float = 2.71828182846;

    static fn square(x: Float) -> Float {
        return x * x;
    }

    static fn cube(x: Float) -> Float {
        return x * x * x;
    }

    static fn max(a: Float, b: Float) -> Float {
        return a > b ? a : b;
    }

    static fn clamp(value: Float, min_val: Float, max_val: Float) -> Float {
        if (value < min_val) {
            return min_val;
        }
        if (value > max_val) {
            return max_val;
        }
        return value;
    }
}

// Using static members
print(MathUtils.PI);           // 3.14159265359
print(MathUtils.square(4.0));  // 16.0
print(MathUtils.cube(3.0));    // 27.0
print(MathUtils.clamp(150, 0, 100));  // 100

this and super

this

Reference to the current instance.

super

Reference to the parent class constructor/methods.