Language basics

This quick-start guide is inspired by Ruby in Twenty Minutes. It makes the assumption that you have Jaguar installed on your machine.

Toit is an open source, object-oriented programming language for the Internet of Things. The Toit language has the following desirable properties:

  • Modern, simple, and approachable
  • High-level and object-oriented
  • Declarative and statically analyzable
  • Safe and garbage collected

Now, let's get started with some programming!

Hello, World

The Toit SDK and Jaguar CLI both support running small programs directly from the command line. If you put the following code in a file called hello.toit:

main:
  print "Hello World!"

you can run it from the command line like this:

$ jag run -d host hello.toit
Hello World!

What just happened? The jag command line tool read your source code (hello.toit) and started running it from the main method that you defined. The main method consists of all the indented statements just below the method declaration line main:. Toit is indentation-based like Python, so the spaces you add to your programs are significant.

Once the program ran, it printed Hello World! in your terminal. This is because the only statement in hello.toit is a method call, where you invoke the print method with a single argument, which is the string to be printed (in this case to the terminal). If you wanted to output more than one line from your program, you could update it to:

main:
  print "Hello World!"
  print "Hello World!"

When you run the updated program, you will see two lines of output:

$ jag run -d host hello.toit
Hello World!
Hello World!

Defining a function

What if you want to say "Hello" a lot without getting your fingers all tired? You should define another function:

hi:
  print "Hello World!"

and call that from main:

main:
  hi
  hi

Calling a function in Toit is as simple as mentioning its name. If the function doesn't take arguments that's all you need.

What if we want to say hello to one person, and not the whole world? Just redefine hi to take a name as an argument.

hi name:
  print "Hello $name!"

This way, hi is a function that takes a single argument. We can use that from main:

main:
  hi "Lars"
  hi "Kasper"

and it works!

$ jag run -d host hello.toit
Hello Lars!
Hello Kasper!

Inserting strings in strings

What's the $name bit? That's Toit's way of inserting something into a string. It is called string interpolation. The bit after the $ is turned into a string (if it isn't one already) and then substituted into the outer string at that point. You can also use this to make sure that someone's name is properly trimmed so leading and trailing whitespace is ignored:

hi name="World":
  print "Hello, $name.trim!"

This way, we call the trim function on the name string before we insert it into the outer string. If we call hi " Lars " we still get the familiar greeting Hello Lars! and not Hello Lars !. You can add parentheses around the name.trim expression in the string to make it clearer which parts belong to the outer string:

  print "Hello, $(name.trim)!"

Maybe you already spotted that we went ahead and added one other trick to the code above? We added a default value for the name parameter, so if the name isn't supplied when you call hi, we use the default name "World". Now we can try:

main:
  hi
  hi "Kasper"

and get the following output:

$ jag run -d host hello.toit
Hello World!
Hello Kasper!

Evolving into a greeter

What if we want a real greeter around, one that remembers your name and welcomes you and treats you with respect. You might want to use an object for that. Let's create a Greeter class:

class Greeter:
  name := null

  constructor .name="World":

  say-hi: print "Hi $name.trim!"

  say-bye: print "Bye $name.trim, come back soon."

The new keyword here is class. This defines a new class called Greeter and a bunch of methods for that class. Methods are just functions that are attached to an object. Pay special attention to the method constructor. There is nothing after the : and the constructor method isn't followed by any indented lines, so the constructor has no statements in it:

  constructor .name="World":

This is a constructor and it defines how you can construct objects from the class. It says the class Greeter takes a single argument (name), but the . prefix to the .name parameter actually tells us that the name is immediately stored as a field on Greeter objects. The field is defined just above the constructor with the := syntax.

The field parameter .name still has a default value, so if we don't pass a name, the Greeter will greet the world.

The say-hi and say-bye methods are introduced on the next two lines. The methods both have a single statement in them, so we can keep them on one line each. The say-hi and say-bye method both use the name field from the object they are called on. You can refer to fields in the class of a method simply by mentioning them (name).

Creating a greeter object

Now let's create a greeter object and use it:

main:
  greeter := Greeter " Helena "
  greeter.say-hi
  greeter.say-bye

We create an object simply by mentioning the constructor, Greeter. The greeter object remembers the name and uses it for the two separate greetings. If we run this, we get the following output:

$ jag run -d host hello.toit
Hi Helena!
Bye Helena, come back soon.

If you want to get the name from a greeter, you can ask a greeter by calling the name method on it:

main:
  greeter := Greeter " Helena "
  print "How are you $(greeter.name)?"

This would show How are you Helena ?. Almost neat, right? Unfortunately, the name isn't trimmed like we expected. Let's fix that!

Fields and methods

As you have just seen, a field on an object introduces a method with the same name. If you wanted to hide a field from the outside world, you could make it private. By convention, methods and fields that end with an underscore (_) are private and not supposed to be touched from the outside:

class Greeter:
  name_ := null
  constructor .name_="World":

This removes the name method from greeters, but if we really want to allow accessing the name from the outside, we could reintroduce a getter with the same meaning as before.

class Greeter:
  name_ := null

  constructor .name_="World":

  name: return name_
  say-hi: print "Hi $name_.trim!"
  say-bye: print "Bye $name_.trim, come back soon."

Here we use the new keyword return to specify the value a method returns. We could make it slightly more interesting and trim it in the process:

  name: return name_.trim

In this way, access to the name from the outside also gets the trimming and we can avoid having to manually call trim when getting the name:

class Greeter:
  name_ := null

  constructor .name_="World":

  name: return name_.trim
  say-hi: print "Hi $name!"
  say-bye: print "Bye $name, come back soon."

We can check that it works by running:

main:
  greeter := Greeter " Erik "
  print "How are you $(greeter.name)?"

and you should see How are you Erik?. greeter is a local variable, only visible in the main method. We declared it with the := syntax, just like we used := to declare member variables in classes.

Greetings everyone!

This greeter isn't all that interesting though, it can only deal with one person at a time. What if we had some kind of MegaGreeter that could either greet the world, one person, or a whole list of people? Let's try to build that. We will start with a class definition:

class MegaGreeter:
  names := []

  constructor name="World":
    names.add name

So MegaGreeter objects have a list of names. The names field is initialized to the empty list ([]). The body of the MegaGreeter constructor adds the given name argument to the end of the list of names. Notice that this is different than using a .name parameter that automatically assigns to the field called name. Mega greeters don't have a single name and no name field, so here the name is just an ordinary parameter that we can use in the body of the constructor. All in all, this code:

main:
  greeter := MegaGreeter
  print "The names are $greeter.names"

will lead to this output:

$ jag run -d host hello.toit
The names are [World]

We can now go ahead and add greeter methods that show all the names:

// Greeter that says hi to everybody.
class MegaGreeter:
  names := []

  constructor name="World":
    names.add name

  say-hi:
    // Greet everyone individually!
    names.do: print "Hello $it!"
  say-bye:
    everyone := names.join ", "
    print "Bye $everyone, come back soon."

main:
  greeter := MegaGreeter
  greeter.say-hi
  greeter.say-bye

  greeter.names.add "Lars"
  greeter.names.add "Kasper"
  greeter.names.add "Rikke"
  greeter.say-hi
  greeter.say-bye

If you run this, you'll get this output:

$ jag run -d host hello.toit
Hello World!
Bye World, come back soon.
Hello World!
Hello Lars!
Hello Kasper!
Hello Rikke!
Bye World, Lars, Kasper, Rikke, come back soon.

Let's dive into the new constructs in the next sections.

Comments and indentation

Not everything in your source files is meant to be run by the Toit compiler. Sometimes, it is nice just to add comments that explain interesting things related to your code. In the example in the last section, there were a few single line comments:

// Greeter that says hi to everybody.
class MegaGreeter:

Such comments start with // and tell the system to ignore the rest of the line.

You have already seen the use of indentation to give hierarchical structure to your code. The general structure is that after a : you can have a single construct if it fits on one line:

class SimpleGreeter:

  say-hi: print "Hi!"  // Method all on one line.

or you can add a newline after the : and let the following lines that are indented relative to the outer construct be a sequence of inner constructs:

  names := []

  // Method delimited by indentation
  say-bye:
    everyone := names.join ", "
    print "Bye $everyone, come back soon."

For methods, we often refer to the inner constructs as the statements of a method or the body of a method. The preferred indentation for inner constructs is two spaces.

For a class, everything that is indented under the class declaration line belongs to the class. We call such things class members:

class MegaGreeter:
  // class members start
  // ...
  // class members end

For methods in a class, the statements in them are nested one level further (two spaces) than the class members:

class MegaGreeter:
  // class members start
  // ...
  say-hi:
    // method body start
    // ...
    // method body end
  // ...
  // class members end

It is common to refer to such nested structure as block structure.

Iterating over lists

Let's return to the MegaGreeter example and take a look at another place where constructs are block structured. In the say-hi method, we want to call print for every single name in the names list. We can do this by calling names.do and provide the list of statements we want to run for each element using block structure:

  say-hi:
    // Greet everyone individually!
    names.do: print "Hello $it!"

Here the statement is on a single line, so there is no need to use indentation. When using names.do, a method available on all collections, the special variable it contains the individual elements from the list in turn. If there are 5 elements in the names list, we will call print 5 times producing 5 separate lines of output.

You can play with the methods on list by modifying and running the sample below:

main:
  list := [ "Horse", "Fish", "Radish", "Baboon" ]
  print "There are $(list.size) elements in the list"
  print "Here they are:"
  list.do: print "Element = $it"

  print "Here they are (sorted):"
  list.sort --in-place
  list.do: print "Element = $it"

One of the methods on lists that is very useful when constructing strings is join. It produces a string from a list of strings by joining the parts and adding a separator between them. We use this in the MegaGreeter example to produce a single comma-separated list of names for the single line output of say-bye:

  say-bye:
    everyone := names.join ", "
    print "Bye $everyone, come back soon."

Named arguments

Perhaps you don't always want to say "Hello", so you add an argument with a default value to the say-hi method:

  say-hi greeting="Hello,":
    // Greet everyone individually!
    names.do: print "$greeting $it!"

Now the user of your class can write:

main:
  greeter := MegaGreeter
  greeter.names.add "Lars"
  greeter.names.add "Kasper"
  greeter.say-hi "Kaixo,"

Which produces the output:

Kaixo, World!
Kaixo, Lars!
Kaixo, Kasper!

However, at the calling site it may not be clear what the argument "Kaixo" is for. We can make it clearer with a named argument:

  say-hi --greeting="Hello,":
    // Greet everyone individually!
    names.do: print "$greeting $it!"

Now we can use this with:

main:
  greeter := MegaGreeter
  greeter.say-hi
  greeter.names.add "Lars"
  greeter.names.add "Kasper"
  greeter.say-hi --greeting="Hej,"

which outputs:

Hello, World!
Hej, World!
Hej, Lars!
Hej, Kasper!

If statements and basic expressions

We can program a ridiculously inefficient Fibonacci sequence generator using if and recursion:

fib n:
  if n <= 1: return n
  return (fib n - 1) + (fib n - 2)

main:
  print "The 10th Fibonacci number is $(fib 10)"

This defines a top-level function called fib that is not a member of any class. (We already saw main, which is a top level function with a special name.)

The fib function is recursive, calling itself, and also makes use of a few new features. The if-statement is well known from other languages. In Toit it works by taking an expression and conditionally evaluating a block. Like other blocks we could have used indentation to group multiple lines.

Toit also has the usual array of infix operators, +, -, *, /, % etc. and the relational operators <, <=, >, >=, == and !=. The operators have higher precedence than function arguments, so we had to group the calls in parentheses to get the desired behavior. The high precedence is what makes the arguments for the recursive invocation of fib work.

Loops

This is a terribly slow way to calculate a Fibonacci number though, and we could do it with a simple loop:

fib2 n:
  s1 := 0
  s2 := 1
  n.repeat:
    s3 := s1 + s2
    s1 = s2
    s2 = s3
  return s1

Here we are using the repeat method on numbers, which runs a block a given number of times. Like for the do method, there's an automatic variable, it that gives the iteration number:

// Prints the numbers from 0 to n (exclusive).
print-n-numbers n:
  n.repeat: print it

The repeat method is simple and efficient, but sometimes we need something more flexible, and for that we have the well-known while and for statements:

// Prints the odd numbers less than n.
print-odd-numbers n:
  for i := 1; i < n; i += 2:
    print i

// Returns if the Collatz conjecture is true.
collatz n:
  while n > 1:
    if n % 2 == 0: n = n / 2
    else: n = n * 3 + 1

Maps and sets

Perhaps we need titles for our greeters:

class MegaGreeter:
  names := []
  titles := {:}

  constructor:

  add name title:
    names.add name
    titles[name] = title
  say-hi:
    // Greet everyone individually!
    names.do: print "Hello, $titles[it] $it!"

main:
  greeter := MegaGreeter
  greeter.add "Lars" "Mr."
  greeter.add "Rikke" "Dr."
  greeter.add "Günter" "Herr Professor Doktor Doktor"
  greeter.say-hi

Here we use a hash map to store the appropriate title for each name. The empty map is given by {:} and we use [] to access the values for each key. The empty set is {} and we already met the empty list, []. The lookup syntax [] also works on lists, so instead of the 'do' method we could have used:

  say-hi:
    for i := 0; i < names.size; i++:
      print "Hello, $names[i]"

Blocks and lambdas

We already saw the repeat method on integers and the do method on lists:

main:
  // Print the numbers from 1 to 10, one per line.
  10.repeat:
    print it + 1
  my-list := [1, 2, 3]
  // Print the elements in my-list, one per line.
  my-list.do:
    print it

Syntactically they look like they are built in to the language like if and for, but they are actually normal methods on the List and int classes:

class List:
  // ...
  do [block]:
    size.repeat: block.call this[it]

class int:
  // ...
  repeat [block]:
    for i := 0; i < this; i++:
      block.call i

They are making use of a feature called blocks. These are snippets of code that can be passed down the stack as arguments to methods and functions. At the call site we precede the block with a colon, ':', and at the function definition we surround the parameter name with square brackets, '[]'. Often, there is one block parameter, it is in the final position and it is called block.

If a callback needs to survive the scope in which it is defined, we can't use blocks. Instead, we can use lambdas:

// Returns a function that adds n to its argument.
add-n n -> Lambda:
  return (:: it + n)

The syntax for lambdas is the same as for blocks, except that the we use :: instead of :. The lambda above is a function that takes a single argument and returns the sum of that argument and n. We can use it like this:

main:
  add-5 := add-n 5
  print (add-5.call 10)

This will print 15.

Blocks and lambdas with multiple arguments

Blocks and lambdas can have parameters, just like methods. The parameters are listed after the :: or :. Here is an example of a block with two parameters:

main:
  map := { "Lars": 1, "Kasper": 2 }
  map.do: | key value |
    print "$key has value $value"

The block in the do method has two parameters, key and value. The do method will call the block with each key-value pair in the map.

Blocks and lambdas that return a value

A block or lambda can return a value each time it is run. This is used for example in the filter method on lists.

// Takes a list of words, and returns a new list with only the
// words that are 5 characters or fewer.
short-words words:
  return words.filter:
    it.size <= 5

The filter method calls the block, it.size <= 5 for each element in the original list, and returns a new list containing only the short words.

Note that there is no return statement in the block. A block will return the value of the last statement to the place where it was invoked with block.call. In this case there is only one statement, which is the boolean expression it.size <= 5.

If you use the return keyword in a block then it returns from the syntactic function or method in which it is written. Usually this will behave as you would expect:

wheres-walter list:
  list.do:
    if it.starts-with "Walter ":
      return it
  return null

main:
  print (wheres-walter ["Ib Michael", "Walter White", "Marie Curie"])

The return keyword is inside a block that is passed to the do method. When the name that starts with "Walter " is found we immediately return the full name from the wheres-walter function without continuing to iterate over the list.