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

Intermediate Features

Summary 

These features are suitable for programmers that already have some programming experience.

They are not necessary in most THT programs.

Optional Parentheses 

Functions that take zero arguments can leave out the empty parentheses.

// With empty arguments
function doSomething() {
    // ...
}

// Same, without parentheses
function doSomething {
   // ...
}

// Either way, functions are always called with parens.
doSomething();

Keyword Shortcuts 

The following one-letter shortcuts can be used instead of the full keyword.

F = function
T = template
R = return

These shortcuts reduce repetition, helping you focus more on the “shape” of your code — i.e. the identifiers that are unique to your program.

Example:

F addNumbers {
    R 1 + 2 + 3;
}

T resultHtml(name) {
    <h1>> Hello {{ name }}!
}

ImportantYour variable and function names should still be clear and descriptive! These are simply shortcuts for keywords that are extremely common.

Quoted (Quick) Lists 

A list prefixed with Q will automatically convert the contents to a list of strings. The tokens are separated by whitespace (spaces or newlines).

let colors = Q[red blue yellow green];
// Same as ['red', 'blue', 'yellow', 'green']

// or separated by newlines
let colors = Q[
    red
    blue
    yellow
    green
];

Operators 

Ternary ? :

The ternary operator is a shortcut for 'if a, then b, else c'.

It is used as an expression to determine a value.

// If/else form
if (condition) {
    a = r1;
} else {
    a = r2;
}

// Ternary form
a = condition ? r1 : r2;

// Examples
let a = true ? 'Yes' : 'No';   //= 'Yes'
let b = false ? 'Yes' : 'No';  //= 'No'

let badge = isVerified ? '(Verified User)' : '';

If/Then >>>

The if/then operator is a shortcut for single-line if statements.

!isAdmin >>> return;

// same as...
if (!isAdmin) { return; }

OR Assign ||=, AND Assign &&=

Logic and assignment can be combined into a single operator.

// OR assign.  Good for assigning a default value.
numGuests ||= 1;

// same as...
if (!numGuests) { numGuests = 1; }

// AND assign.  Good for processing a value further.
date &&= formatDate(date);

// same as...
if (date) { date = formatDate(date); }

OR Value ||:, AND Value &&:

Instead of evaluating true/false, logical value operators return the value of either side.

// OR value.  Good for a assigning a default value.
let id = user.id ||: -1;

// same as...
if (user.id) {
    id = user.id;
} else {
    id = -1;
}

// AND value.  Good for checking if an object is valid.
let id = user &&: user.id;

// same as...
if (user) {
    id = user.id;
} else {
    id = user; // falsey value
}

Splat ...

The ... prefix operator can be used to pack and unpack function arguments to/from Lists.

// Combine remainder arguments into a single List.
function doSomething(first, ...rest) {
    print(rest);
}

doSomething(0, 1, 2, 3);
//= [1, 2, 3]
function doSomething(first, second, third) {
    print(first + second + third);
}

// Expand a List into multiple arguments
let args = [11, 22, 33];
doSomething(...args);
//= 66

Bitwise Operators

Because bitwise operations are rarely used in web development, THT uses non-standard operators (borrowed from Perl 6) to avoid accidental confusion with their Logical counterparts (e.g. & vs. &&).

+| = bitwise OR
+& = bitwise AND
+^ = bitwise XOR
+~ = bitwise NOT
+> = bitwise SHIFT RIGHT
+< = bitwise SHIFT LEFT

These can be used with literal binary digits with the 0b prefix.

Example:

let bin = 0b100 +& 0b110;
//= 4 (0b100)

Spaceship <=>

The spaceship operator compares two numbers and returns -1, 0, or 1, depending if the 1st number is less than, equal, or greater than the 2nd number.

This is primarily used for sorting functions.

1 <=> 2  //= -1
1 <=> 1  //=  0
2 <=> 1  //=  1

let letters = Q[ x y z ];
letters.sort(F (a, b) { R b <=> a; }

//= ['z', 'y', 'x']

Cat ^^

The cat ^^ prefix operator applies advanced opti-meow-zation to a variable.

let moneyInBank = 30001.67;
print(^^moneyInBank);
//= 'MEOW'

Regular Expressions 

Some string methods take a regular expression (aka “regex”) string as an argument.

Regex strings are prefixed with a lowercase 'r'. (e.g. r'[a-z]+').

Compared to normal strings, backslashes are treated literally.

See PHP's Regular Expression Docs for more.

'123 street'.hasMatch(r'\d+ \w+');
//= true

// case insensitive match
'Hello'.hasMatch(r'hello'.flags('i'));
// true

// Match with capturing (...)
'key=123'.match(r'(\w+)=(\d+)');
//= ['key', '123']

Anonymous Functions 

You can assign a standalone function directly to a variable. This allows it to be passed into other functions, enabling functional-style programming.

THT looks for a fn- prefix in the variable name to know that it is a function at runtime, instead of a standard function name.

let fnDouble = F (num) { R num * 2; };
fnDouble(8);
//= 16

let numbers = [1, 2, 3];
numbers.map(fnDouble);
//= [2, 4, 6]

Closures

A closure is a type of anonymous function that keeps a local variable in scope when the function is declared. It can then be accessed when the function is later called.

Use the keep keyword to retain a local variable.

F createLogger(messageType) {

    let fnLog = F (message) keep (messageType) {
        print(messageType ~ ': ' ~ message);
    };

    return fnLog;
}

let fnLogUserEvent = createLogger('user');
fnLogUserEvent('logged in');
//= 'user: logged in'

let fnLogSystemEvent = createLogger('system');
fnLogSystemEvent('shut down');
//= 'system: shut down'

Argument Typing 

You can declare the type of each argument in a function. Calls that don’t match these types will fail at runtime.

To declare a type, append a type token to the argument with a colon :.

Types are strictly enforced. They are not coerced except that an integer may be given to a function expecting a float.

This feature is only available in PHP 7.1+. For earlier versions of PHP, types will be ignored.

// Take an integer and a string
function countItems(num:i, itemName:s) {
    print('You have ' ~ num ~ ' ' ~ itemName);
}

countItems(3, 'apples');
//= 'You have 3 apples'

countItems('apples', 3);
// ✖ ERROR. Arguments are reversed.

Type Tokens:

s   = string
b   = boolean
i   = integer
f   = float
l   = list
m   = map
o   = object
fn  = function
any = all types

The any type is the same as delaring no type at all. It is used to explicitly document that no type is enforced.

Try/Catch 

When something goes wrong, it’s usually better to let the program die. A dead script can’t do any more damage, and is less likely to become a security vulnerability.

However, sometimes you want to provide a fallback for specific errors.

An Exception is an error that can be trapped with try/catch. The catch block will only run if there is an Exception in the try block.

let content = '';
try {
    // Will throw an Exception if it can't read the file.
    content = File.read('important.txt');
} catch (e) {
    // Error reading the file.  Try to restore it and continue.
    content = getDefaultContent();
    File.write('important.txt', content);
}

finally

A finally block can be used to define code that will run regardless of whether or not an Exception occurred.

This is optional, and is usually intended for cleanup logic that is invoked before the program ends.

try {
    // initial code
} catch (e) {
    // run if an Exception happens
    print(e.message());
} finally {
    // always run this after the above blocks complete
}

TipThis is usually unnecessary, thanks to PHP's atomic request cycle. Each THT request is automatically cleaned up after it ends (e.g. closed file handles and database connections).

die

You can trigger an Exception manually with die.

if (!fileExists) {
    die('Important file not found.');
}

To halt the program without triggering an Exception, use System.exit().