THT is in early Beta. We welcome contributors & feedback.

Language Tour  (30 Minutes)

A Very Simple Program 

Let’s start with a one-line program.

print('Hello! :)');

As you can probably guess, it prints this to the browser:

Hello! :)

How It Works

Let’s look at each part.

// "print" is the command, or "function".
print('Hello! :)');
^^^^^

// Our text is passed into the function by
// surrounding it with parentheses.
print('Hello! :)');
     ^           ^

// A "string" of text is surrounded by single
// quotes (which are not printed).
print('Hello! :)');
      ^^^^^^^^^^^

// Every statement ends in a semicolon, just
// like a period at the end of a sentence.
print('Hello! :)');
                  ^

JargonIn the correct terminology, we’d say we are “calling the 'print' function, with the argument 'Hello! :)'”

Printing 

The print function is like a temporary guidepost to check your code as it runs. It’s mainly for you, as the developer, to experiment and debug.

If you try to print a data type other than text, it will be displayed in a readable format.

To send HTML to the browser for your users, you will use the Web module, as described in How to Create a Basic Web App.

Format Checker 

Messy code can be frustrating and stressful to work with. It also creates places where mistakes can hide.

To help you write clean code, THT includes a Format Checker. It provides instant feedback about how to format your code so that it’s easier to read and work with.

Spacing is especially important!

// ✖ NO
lineWidth=(10+5)~'px';

// ✔ YES  Cleaner spacing
lineWidth = (10 + 5) ~ 'px';

See Format Checker for more information.

Comments 

A comment is a line starting with //. It’s not executed as part of the program. It can appear on a line by itself, or at the end of a code line.

Comments are useful for a few things:

Explaining code. Explain parts of the program to help any future reader (including you!) modify it later.

// Apply the Zuckerdorf Friendship Formula to determine how
// close this friend is to the current user.
let friendLevel = (numFriendsInCommon * 1.2345) + 42;

You don’t need a comment if the code is self-explanatory.

// Set number of apples    ✖ NO, this is redundant
let numApples = 1;

Disabling code. Temporarily remove lines of code while debugging.

// showWidgets(widgets);

TODO's. Record “To Do” reminders for later.

// TODO: add in the rest of the seven dwarves
let dwarves = ['Sleepy', 'Sneezy', 'Doc'];

Multiline Comments

Surrounding text with /* and */ creates a multi-line comment block.

/*
    TODO:
    - update header styles
    - fix profile bug
    - add About page
*/

The Right Balance

TipComments are important, but it’s also possible to have too many of them. This can get in the way of reading the actual code.

Instead of writing long comments, try to make the code itself more self-explanatory, by using clearer variable names, separating code into small functions, etc.

Variables 

A variable is like a box that contains information. You can give it a label and change what is inside.

Variables are declared (created) with the let keyword. A variable must be declared before it is used.

Data values are assigned to variables with =.

let location = 'Paris';
let numArtists = 9000;
let isBeautiful = true;

// ✔ OK: 'location' was declared above, so it can be changed.
location = 'Prague';

// ✖ ERROR: 'isExpensive' was never declared with 'let'.
isExpensive = false;

Case Matching

Variable and function names are case-sensitive. They must match exactly.

let numApples = 10;
numapples = 11;  // ✖ ERROR: 'A' should be uppercase.

Print('Hello!');  // ✖ ERROR: 'print' should be lowercase.

Basic Data Types 

There are 3 basic types of data:

// Numbers
42     // positive
-62    // negative
1.618  // fraction (aka 'floating point' or 'float')

// String (single-quotes)
'This is a string!'

// Flags
true
false

Initializing Variables 

New variables must always be given a value.

If you’re not sure what to use, pick the “empty” version of the type you need:

let myString = '';
let myNumber = 0;
let myFlag = false;

Variable Names 

Names for variables and functions are always written in camelCase format:

Examples:

user
userId
firstName
isDeleted
numRecords
httpVersion
userHasIphone
convertJsonToXml

Naming Tips

Coming up with good names is one of the most important (and difficult) skills in programming.

Always take the extra time to think of names that clearly represent what they are labelling.

Guidelines:

Examples:

✖ Bad         ✔ Better
------        ------------
usr           user
fname         firstName
nbrUsers      numUsers
cmntTxt       commentText
banned        isBanned
activeTime    numDaysActive
distance      numMiles
userList      users
active        activeUsers
a             (be more specific)
temp          (be more specific)

Strings 

Strings are always surrounded by single quotes.

'Hello World!';  // ✔ OK

"Hello World!";  // ✖ ERROR: Double-quotes are not valid.
Hello World!;    // ✖ ERROR: 'Hello' is an unknown command.

Combining Strings

Strings can be combined with the stringy operator ~ (tilde).

let numPaintings = 12;
let firstName = 'Dan';
let lastName = 'Gogh';

let fullName = firstName ~ ' ' ~ lastName;
//= 'Dan Gogh'

print(fullName ~ ' has ' ~ numPaintings ~ ' paintings.');
//= 'Dan Gogh has 12 paintings.'

Multi-line Strings

Multi-line strings are surrounded with triple-quotes '''.

The quotes must appear on separate lines.

Surrounding whitespace and extra indentation will be automatically trimmed, so you can keep the formatting consistent with the surrounding code.

let poem = '''
    Roses are red,
    violets are blue,
    THT is beautiful,
    and so are waffles.
''';

Special Characters

For convenience, backticks ` are converted to single-quotes.

'That`s amazing!';  //= 'That's amazing!'

Special characters can be used in strings, using backslash \.

'This is line 1.\nThis is line 2.'   // This is line 1.
                                     // This is line 2.

'Green\t#00FF00'  //= 'Green   #00FF00'
'Red\t#FF0000'    //= 'Red     #FF0000'

Escapes

Use a backslash \ to make a character appear as-is.

'That\'s amazing!'  //=  'That's amazing!'

'Backslashes (\\) and forward slashes (/)'
//= Backslashes (\) and forward slashes (/)'

LockStrings 

Injection attacks are the #1 security vulnerability on the web.

THT introduces LockStrings to mitigate this risk. Just prepend 'L' to any literal string to mark it as safe. (e.g. L'my string')

LockStrings can not be modified in ways that would expose them to insecure data (e.g. user input from a web form.)

Sensitive operations (printing HTML, database access, system calls, etc.) all require LockStrings as input.

// ✖ DANGEROUS
// A value like this from attacker would add
// a statement that deletes your 'users' table.
let userId = '1; drop table users;';

let sql = 'select * from users where userId = ' ~ userId;

// ERROR - plain strings are unsafe
let row = Db.selectRow(sql);


// ✔ SAFE
// Create a LockString and attach the value as a placeholder.
let sql = L'select * from users where userId = {}';
sql.fill(userId);

// The Db module knows how to safely escape placeholder values.
let row = Db.selectRow(sql);

You can join two LockStrings together, even with placeholders.

let lock1 = L'Value 1: {}\n';
let lock2 = L'Value 2: {}\n';

let combined = lock1 ~ lock2;

print(combined.fill(['a', 'b']));
//= Value 1: a
//= Value 2: b

See the LockString class for a list of methods.

Math Operators 

Numbers can be modified with the math operators + - * /.

Expressions can be grouped together with parentheses ( ).

let numPosts = 5;
let numUpvotes = 7;
let numKarma = (numPosts * 2) + numUpvotes;

print('You have ' ~ numKarma ~ ' karma points!');
//= 'You have 17 karma points!'

Combined Operators

Operators can be combined with assignment =, as a convenient shortcut.

let numKarma = 5;

// Add 2 karma (the long way)
numKarma = numKarma + 2;  //= 7

// The short way
numKarma += 2;

message = 'Karma: ';
message ~= numKarma;
//= 'Karma: 7'

// More examples
a += 2;  // a = a + 2;
a -= 2;  // a = a - 2;
a *= 2;  // a = a * 2;
a /= 2;  // a = a / 2;

Comparison Operators 

Comparison operators compare two values, and return true or false.

let a = 1;

a == 1;  //= true  (equal)
a != 5;  //= true  (not equal)
a == 2;  //= false
a != 1;  //= false

a > -1;  //= true  (greater than)
a >= 1;  //= true  (equal or greater than)
a < 5;   //= true  (less than)
a <= 0;  //= false (equal or less than)

let name = 'Ann';
name == 'Ann';   //= true
name == 'Ann ';  //= false (extra space)
name != 'Bob';   //= true

TipTry not to confuse = (assign) with == (compare). THT will try to prevent this common mistake, but be careful.

Conditional Statements 

if Statement

The if statement executes a block of code if the condition is true. A block of code is surrounded by curly braces { }.

if (condition) {
    // code to run if condition is true
}

// Example
if (numCats > 100) {
    print('It`s a cat-astrophe!');
}

if / else Statement

The else statement executes a block of code if the condition is false.

if (condition) {
    // code to run if condition is true
} else {
    // code to run if condition is false
}
// Example
if (numGold >= priceOfHat) {
    print('You can buy the hat! :)');
} else {
    let needNumGold = priceOfHat - numGold;
    print('You need ' ~ needNumGold ~ ' more gold. :(');
}

else if Statement

The else and if statements can be chained together.

if (condition1) {
    // code to run if condition1 is true
} else if (condition2) {
    // code to run if condition1 is false & condition2 is true
} else if (condition3) {
    // multiple "else ifs" are supported
} else {
    // code to run if all conditions are false
}
// Example
if (score == 0) {
    print('Did you even try? :(');
} else if (score > highScore) {
    print('You set a new high score! :)');
} else {
    print('Try again. :|');
}

Logical Operators 

Logical operators let you join comparison operators in two ways: && (and) and || (or).

// AND - Both conditions must be true
if (ageYears < 13 && movieType == 'horror') {
    print('You should probably watch something else.');
}

// OR - Either condition must be true
if (heightCm < 120 || isAfraidOfHeights) {
    print('Do not enter this ride!');
}

Methods 

Methods are functions that are attached to data. They are called with the dot . operator.

All built-in data types have methods.

let phrase = 'Red Rover  ';

phrase.trim();            //= 'Red Rover' (no extra spaces)
phrase.contains('Red');   //= true
phrase.length();          //= 14

Modules

A module is a bundle of related functions. Module names are always UpperCamelCase.

THT comes with essential modules like Math, Date, and Web.

Math.floor(3.15);        //= 3 (rounded down)
Math.pi();               //= 3.1415926535898
Date.now();              //= current timestamp in seconds
File.read('words.txt');  //= contents of file

ReferenceAll built-in modules and methods are listed in the Manual.

Lists 

A List is a variable that can hold multiple values, separated by commas.

// a list of strings
let letters = ['a', 'b', 'c'];

// A list of numbers
let ages = [0, 2, 4, 6, 8, 10];

// Multi-line format
let countries = [
    'Argentina',
    'Brazil',
    'Chile'
];

// Each item can be an expression
let nums = [1 + 9, 2 + 8];

JargonIn other languages, Lists are sometimes called Arrays.

List Elements

You can access elements of a list by the index number of its position, which always starts at 0.

let letters = ['a', 'b', 'c', 'd'];

let first = letters[0];  //= 'a'
let second = letters[1]; //= 'b'

// Negative indexes start counting from the end
let last = letters[-1];        //= 'c'
let nextToLast = letters[-2];  //= 'd'

// Modify an index directly
letters[0] = 'yay!';
print(letters);
//= ['yay!', 'b', 'c', 'd']

List Methods

Here are some common List methods. See List for all methods.

let colors = ['red', 'blue'];

colors.contains('blue');  //= true
colors.length();          //= 2
colors.join(' & ');       //= 'red & blue'

// Add item to the end
colors.add('yellow');
//= ['red', 'blue', 'yellow']

// Add item to the beginning (at index 0)
colors.add('orange', 0);
//= ['orange', 'red', 'blue', 'yellow']

// Remove item from the end and return it
colors.remove();  //= 'yellow'
//= ['orange', 'red', 'blue']

// Remove item from beginning (at index 0)
colors.remove(0);  //= 'orange'
//= ['red', 'blue']

Maps 

A Map is a collection of key/value pairs. It’s like a List, but instead of having a specific order (0,1,2,3), each item corresponds (or “maps”) to a String key.

Maps are surrounded by curly braces { }. Each key/value pair is joined by a colon :. Pairs are separated by commas ,.

// A one-line Map
let user = { name: 'Violet', age: 35 };

// An empty Map
let user = {};

// Multi-line Map
let user = {
    name: 'Violet',
    age: 35,
    job: 'Florist',
};

// Assign a new key
user['address'] = '123 Rainbow Road';

// Get fields with [...] notation or dot notation
user['name'];   //= 'Violet'
user.name;      //= 'Violet'

// Dot notation is more strict with missing keys
user['email']; //= '' (a safe falsey value)
user.email;    // ✖ ERROR - 'email' is not defined

TipThink of a List as a collection of many things, while a Map holds the properties of a single thing. (It’s very common to have a List of Maps. e.g. rows from a database query.)

JargonIn other languages, Maps are called many different things: associative arrays, dictionaries, hash maps, hashes, or records. They all basically mean the same thing: a set of key/value pairs.

For Loops 

A for loop repeats the same block of code multiple times. They are most commonly used to iterate over a List.

// Loop over a list
let colors = ['red', 'blue', 'yellow'];
for (color in colors) {
    print(animal.toUpperCaseFirst() ~ ' is a primary color.');
}
//= 'Red is a primary color'
//= 'Blue is a primary color'
//= 'Yellow is a primary color'

// Loop over a range of numbers.
for (n in range(0, 2)) {
    print(n ~ ' = ' ~ colors[n]);
}
//= '0 = red'
//= '1 = blue'
//= '2 = yellow'

Loop Control

Use break to immediately exit a loop. The program will jump to the end of the block and resume.

Use continue to skip the current cycle of a loop. It will immediately jump to the top of the block and resume the next cycle.

// Find the 1st quote that refers to 'inspiration'
let lines = File.read('quotes.txt');
let foundLine = '';
for (line in lines) {
    if (line == '') {
        continue;  // skip to the next cycle
    }
    if (line.contains('inspiration')) {
        foundLine = line;
        break;  // end the loop
    }
}
print(foundLine);

Forever Loops

A bare for loop will keep repeating until you exit via break.

This is useful when you don’t know how many times it will repeat.

// Print all quotes that refer to 'curiosity'
let file = File.open('quotes.txt');
for {
    let line = file.readLine();
    if (line.contains('curiosity')) {
        print(line);
    }
    // end of file
    if (line == false) {
        break;
    }
}

JargonOther languages achieve this with the while and do/while commands. THT's approach is simpler, and avoids some common pitfalls with those commands (such as off-by-one bugs).

Functions 

A function is a block of code that can be re-used anywhere else in the program.

When you call a function, you can pass in zero or more arguments.

// A function with no arguments
function sayHi() {
    print('Hi!');
}

// With a 'message' argument
function saySomething(greeting) {
    print(message ~ '!');
}

// Call the functions
sayHi();                //= prints 'Hi!'
saySomething('Hello');  //= prints 'Hello!'
saySomething();         // ✖ ERROR - Needs one argument.

Arguments can have default values if they aren’t passed in.

// The 'greeting' argument has a default value
function saySomething(to, greeting = 'Hello') {
    print(message ~ ', ' ~ to ~ '!');
}

saySomething('Bob');         //= prints 'Hello, Bob!'
saySomething('Bob', 'Hey');  //= prints 'Hey, Bob!'

Template Functions 

Template functions let you include multi-line strings directly in your script.

These are mainly used for blocks of HTML, CSS, and JavaScript.

They support extra syntax, like double-braces {{ ... }} for embedding THT expressions.

See Templates for more info.

template html(userName) {

    <h1>My Web Page</h1>

    <p>Hello {{ userName }}!</p>

}

Scope 

Variables are only accessible inside the block they are defined in. This includes functions, if/else blocks, and for loops.

Variables in the top-most scope are not available inside functions.

let myVar = 123; // in the top-level scope

function doSomething() {
    print(myVar); // ✖ ERROR
}

Variables in one function are not available in other functions.

function doSomething() {
    let myVar = 123;
}

function doSomethingElse() {
    // ✖ ERROR - in a different function
    print(myVar);
}

Variables in inner blocks are not available in the outer scope.

if (true)
    let myVar = 123;  // inner block
}

print(myVar); // ✖ ERROR

Custom Modules 

Modules let you organize functions into separate files, which can be included from any page.

Module names are always UpperCamelCase, and go in the modules folder.

// file: modules/MyModule.tht

function sayHello(userName) {
    print('Hello, ' ~ userName ~ '!');
}

To call a function in a module, use dot . notation.

// file: pages/myPage.tht

MyModule.sayHello('Mr. Yellow');
//= 'Hello, Mr. Yellow!'

Modules in the root modules folder will be automatically included when they are used.

Modules in sub-folders need to be pulled in via import before they are used.

// pull in file 'modules/profiles/SnailProfile.tht'
import('profiles/SnailProfile');

SnailProfile.getSpeed(); //= '3 feet per hour'

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 share-nothing 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().