Version: v0.5.0 - Beta.  We welcome contributors & feedback.  THanks!

Classes & Objects

Work in Progress 

THT currently has a minimal but flexible approach to object-oriented programming.

We intend to support more of PHP's object-oriented functionality in the future.

Shortcuts 

This tutorial uses the shortcuts for function (F) and this (@).

These are for readability only. They are one-to-one replacements and have no impact on performance.

Creating Objects 

Classes are defined within module files.

When instantiating a class in the root modules folder, it will be auto-loaded.

//--- file: modules/MyClass.tht ---

class MyClass {
    // ...
}

//--- file: pages/home.tht ---

let object = new MyClass();

Use import to refer to a class in a subfolder.

//--- file: modules/utils/Logger.tht ---

class Logger {
    // ...
}

//--- file: pages/home.tht ---

import('utils/Logger.tht');

let logger = new Logger();

Fields 

Fields are directly assigned to @ (shortcut for this) within the constructor (new), which is called when the object is created.

//--- file: modules/MyClass.tht ---

class MyClass {

    F new(num) {
        @.number = num;
        @.color = 'red';
    }

}

//--- file: pages/home.tht ---

let object = new MyClass(123);

print(object.number);
//= 123

print(object.color);
//= 'red'

Private State 

In the current version of THT, all functions and fields are public (accessible to outside callers).

As a temporary solution, THT automatically adds a private state field to all classes, to which you can assign a Map.

//--- file: modules/Counter.tht ---

class Counter {

    F new {
        @.state = {
            count: 0
        };
    }

    F add {
        @.state.count += 1;
    }

    F getCount {
        return @.state.count;
    }

}

//--- file: pages/home.tht ---

let counter = new Counter();

counter.add();
counter.getCount();
//= 1

print(counter.state.count);
//= ✖ ERROR: state is private

Private Naming (optional) 

You may use the convention of prepending 'x' to your method names (e.g. xMyFunction) to signal that they should be considered private.

This is simply a naming convention — it does not impact visibility.

Composition 

Inheritance via the extends keyword isn’t supported yet.

Instead, you can use composition to re-use code across classes.

Composition is the technique of containing other objects within your object. You can then send method calls to these inner helpers as needed.

TipIt is considered a good practice to prefer composition instead of inheritance. Composition lets you include the functionality you need, without locking yourself into a strict heirarchy.

class User {

    F new(userId) {
        // An inner object
        @.logger = new Logger();

        @.userId = userId;
    }

    F log(message) {
        // Calling the inner object
        @.logger.log(@.userId ~ ': ' ~ message);
    }

    F login() {
        @.log('Logged in');
    }
}

Static Methods & Fields 

Because each class is contained within a module, you can use module-level scope to implement static functionality.

To emulate static methods, simply define functions within the same module, outside of the class.

To emulate static fields, you can assign data directly to the module.

TipThe @@ symbol can be used as shorthand for the current module.

//--- file: modules/MyClass.tht ---

// Module-level variable
MyClass.xNumObjects = 0;

F numObjects {
    // '@@' is just a shortcut for 'MyClass'
    return @@.xNumObjects;
}

class MyClass {

    F new {
        @@.xNumObjects += 1;
    }
}


//--- file: pages/home.tht ---

let object1 = new MyClass();
let object2 = new MyClass();

print(@@.numObjects());
//= 2

Auto Getters & Setters 

You can automatically override access to fields with methods that follow the getFieldName and setFieldName convention.

class MyClass {
    F new(userId) {
        @.userId = userId;
    }

    F getUserId() {
        return 'id:' ~ @.userId;
    }

    F setUserId(id) {
        if (id < 0) {
            die('userId can not be negative!');
        }
        @userId = id;
    }
}

let user = new User(123);
print(user.userId);
//= 'id:123'

user.userId = -99;
// ✖ ERROR

Meta-Methods 

All objects inherit special methods for dynamically handling fields and methods.

See Object.