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 World!');

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

Hello World!

How It Works

Let’s look at each part:

1) print is the command, or function.

print('Hello World!');
^^^^^

2) A string of text is surrounded by single quotes. (The quotes are not printed.)

print('Hello World!');
      ^^^^^^^^^^^^^^

3) Our string is passed into the print function by surrounding it with parentheses.

print('Hello World!');
     ^              ^

4) Every statement ends in a semicolon, just like a period at the end of a sentence.

print('Hello World!');
                     ^

JargonIn the correct terminology, we’d say we are calling the 'print' function, with the argument 'Hello World!'

Printing 

The print function is a quick way 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 

In the sometimes chaotic process of getting your programs to run, it’s easy to end up with code that is a bit messy.

Messy code can be frustrating and stressful to work with, because it is harder to read and creates places where bugs 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 (data). Each variable has a label, and you can put different data in it any time.

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
nbrPosts      numPosts
cmntTxt       commentText
banned        isBanned
activeTime    numDaysActive
distance      distanceKm
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.

Any operation that takes a freeform string as input is at risk of being tainted by invalid or hostile user input.

The most common targets of attack are:

To mitigate this risk, THT introduces LockStrings, which are literal strings that can only be combined with dynamic (i.e. potentially unsafe) strings through the use of placeholders.

Placeholders are one of the industry’s best practices for building SQL queries, but LockStrings expand this tactic to cover all sensitive strings.

This approach provides end-to-end protection because:

How LockStrings Work

Just prepend L to any literal string to mark it as a LockString.

Example:

let sql = L'select * from users';

LockStrings can not be modified like regular strings, so they can not accidentally be combined with insecure data.

Example:

// A malicious value via form input that attempts to
// delete your table
let userId = '1; drop table users;';

// ✖ ERROR - combining LockString with a normal string
let sql = L'select * from users where userId = ' ~ userId;

Placeholders

You can attach dynamic data to a LockString via the fill method. These will be safely inserted into placeholders (e.g. {}).

THT modules that perform sensitive operations (e.g. Db, System, Web) only accept LockStrings as input. They take responsibility for escaping placeholder values so you don’t have to worry about it.

Example:

let sql = L'select * from users where userId = {}';
sql.fill(userId);
let row = Db.selectRow(sql);

Appending LockStrings

You can join two LockStrings together. Placeholder values will be merged into one list.

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

Methods

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'

The methods push and pop treat the List as a stack of items. They add and remove the last item in the List.

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

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

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

The methods insert and remove let you add and delete items anywhere in the List.

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

// Add item to the beginning (at index 0)
colors.insert('orange', 0);
//= ['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.

Map Methods

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

let user = {
    name: 'Violet',
    age: 35,
    job: 'Florist',
};

user.keys();
//= ['name', 'age', 'job']

user.hasKey('job'); //= true

user.remove('job');
//= { name: 'Violet', age: 35 }

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'

Module Variables 

You can assign variables directly to a module.

These act as a namespaced global variable, accessible anywhere within your app.

// file: modules/MyModule.tht

MyModule.colors = {
    red:   '#FF0000',
    green: '#00FF00',
    blue:  '#0000FF',
};

function getRedHex() {
    return MyModule.colors.red;
}


// file: pages/colors.tht

function getColors() {
    return MyModule.colors.keys();
}