Style guide

This document describes the preferred style for the Toit language. To learn about Toit's syntax see the syntax summary. To learn about Toit's documentation convention, see the language documentation convention section.

Whitespace and indentation

Indentation in Toit is always with spaces. Tabs are not allowed.

We use two spaces for statements.

j := 10

main:
  for i := -10; i < j; i++:
    print i
    i = i + 1

When parameters are indented, they should be intented by four spaces:

fun-with-many-params
    param1 /int
    param2 /string
    param3 /Lambda:
  return ...  // Code is again intended by 2.

Arguments are intended by four spaces, unless they are the body of a block or lambda:

main:
  fun-with-many-params
      1
      "string arg"::
    // Body of lambda is only intended by 2.
    it + 1

The same applies for operators or other reasons when a line gets too long:

my-function x/num y/num -> num:
  return x * x * x
      + y * y * y

Line length

There is no hard line length limit in Toit, but it is often more readable to limit line lengths to 80 or 120 characters.

Block comments can be line-wrapped at any point and should almost always be limited to 80 characters for readability, even in code that is wider for some reason.

When breaking longer lines, the point of lowest precedence should be picked.

my-badly-formatted-function x/num y/num -> num:
  return x * x * x + y * y   // Don't break at high-precedence '*'.
      * y

my-well-formatted-function x/num y/num -> num:
  return x * x * x           // Break at low-precedence '+'.
      + y * y * y

As seen above prefer to break before operators, not after operators.

This fits well with the ability to use newlines in long argument lists, since argument lists have lower precedence than operators.

  // Using newline instead of parentheses to delineate the
  // single argument to the print function.
  print
    unary-function argument

Whitespace around operators

Always surround operators with whitespace.

  x1:=y+1  // Not enough spaces.
  x2 := y + 1  // Better spacing.
  radians-1:=degrees*2*math.PI/180  // Not enough spaces.

  radians-2 := degrees * 2 * math.PI / 180  // Better spacing.

Arguments to functions and methods also have single spacing.

fib n:
  if n < 2:
    return 1
  recurse := fib n - 1
  return n * recurse

For functions with more than one argument this can be hard to read, in which case you should add parentheses or newlines.

  my-func x+1 x*1          // Not enough spaces.
  my-func x + 1 x * 1      // Hard to read.
  my-func (x + 1) (x * 1)  // Easier to read.
  my-func                  // Nice uncluttered look with newlines.
      x + 1
      x * 1

Between a line and an end-of-line comment there should be at least two spaces. More can be added for alignment (see above).

Naming

Most names in Toit are named with lower letters and dashes between words.

// Top level function.
my-function:
  // Local variable
  my-variable := 0

Classes are named with initial capital letters and camel case.

class MyClass:

This applies even if they contain abbreviations that would normally be written in all-caps.

class MyRgbToYuvConverter:

Constants are written in all-caps with dashes:

FAHRENHEIT-MODE ::= 0
CELCIUS-MODE ::= 1
KELVIN-MODE ::= 2

We prefer not to use abbreviations in API-visible names unless they are very well established.

// Name too abbreviated.
f-to-c f/num -> num:
  return (f - 32.0) / 9 * 5

// Better.
fahrenheit-to-celcius fahrenheit/num -> num:
  return (fahrenheit - 32.0) / 9 * 5

An exception must be made for variables named str since the name string is taken for the string type, but often a better name than str can be found.

Package-private names end with an underscore.

Comments

A single line comment is written as a full sentence, starting with a capital letter and ending with a period (.). At least two spaces should separate the // from code, and a space separates the // from the text of the comment.

//bad

// This is a single line comment in Toit.

See also documentation comments.

Declarations

Variables and members are not given an initial value unless it makes sense.

  z := some-value
  x := 0      // No need to give x an inital value.
  y /int := ?  // Compiler will check it is initialized before being used.
  if z == 1:
    x = 1
    y = 2
  else:
    x = 2
    y = 1

Similarly for members:

class Foo:
  x /int? := null  // Don't assign null unless you want this default
  y /int  := ?     // Value comes from constructor.

  constructor .x .y:  // Compiler will check that y is initialized.

Classes

Getters and setters

In Toit, getters and setters have the same syntax (for users of a class) as public member variables. Therefore there is no need to write trivial setters and getters:

class UnneededPrivacy:
  // No point in making this private since it has a trivial
  // getter and setter.
  x_ /int := ? // Private member variable.

  constructor x/int:
    x_ = x

  // Getter.
  x -> int:
    return x_

  // Setter.
  x= value/int -> none:
    x_ = value

Instead just make the member public:

class Simple:
  x/int := ?

  constructor .x:

If you later need to intercept the setter (for example for logging), you can always change the Simple to the implementation used by UnneededPrivacy without having to change users of the class.

Types

Elements of public APIs should be typed. Internal variables should be typed where it aids readability and according to taste. It is often useful to type parameters, as they cannot be inferred by the compiler.

Types in argument lists are written without spaces in order to group the arguments visually:

my-function x/int y/float z/string -> none:
  // Implementation

Types of instance variables and locals are written with a space before the /:

class Foo:
  x /int
  y /string

  constructor .x .y:

Literals

For list literals on multiple lines put a comma on every line, even the last:

RGB ::= [
  0.0,
  50.0,
  99.9,
]

As seen above, the closing bracket is dedented to the initial indentation.

If all values are in the 0-255 range and you don't expect to modify the list with a non-byte value, consider using a ByteArray literal:

RGB ::= #[
  0,
  0x80,
  0xff
]

Loops

For simple zero-to-n loops prefer repeat to for-loops.

  for i := 0; i < n; i++:  // No need for a for-loop.
    print i

  n.repeat:  // Easier to read and actually faster.
    print it

If possible use the iteration built-in to the collection:

  for i := 0; i < list.size; i++:  // Not using the collection's own iterator.
    print list[i]

  list.size.repeat:  // Not using the collection's own iterator.
    print list[it]

  list.do:  // Good.
    print it

In loop bodies more than one or two lines, give the iteration variable a name instead of just using it:

  list.do: | element |
    if element >= 0:
      print element