Keywords

This page covers the building blocks that shape how your program makes decisions and repeats work: conditions, loops, breaks, and a few loop helpers that make common patterns shorter. Every example is verified with an assert so you can see the exact expected result.

fn main() {

Conditionals

if runs a block only when its condition is true. If the condition is false the block is skipped entirely — nothing else happens. panic stops the program immediately with a message. During development it is a clear way to mark situations that should never occur.

  if 2 > 5 {
    panic("Incorrect test");
  }

Combine conditions with and / or (or their symbol equivalents && / ||). Note that & is the bitwise AND operator — it works on the individual 1s and 0s inside a number, which is different from the logical and keyword that works on true/false values. An if/else chain picks exactly one branch to execute.

  a = 12;
  if a > 10 and a & 7 == 4 {
    a += 1;
  } else {
    a -= 1;
  }

Text enclosed in double quotes can contain expressions inside curly braces — the expression is evaluated and its result is inserted into the text. assert checks that its first argument is true; if it is false the second argument is printed as an error message.

  assert(a == 13, "Incorrect value {a} != 13");

if as an expression

Every block in Loft produces a value — the last expression inside it. That means you can use if/else on the right-hand side of an assignment, picking between two values based on a condition. No ternary operator needed.

  b = if a == 13 {
    "Correct"
  } else {
    "Wrong"
  };
  assert(b == "Correct", "Logic expression");

Iteration

for x in a..b loops over the integers starting at a and stopping before b (exclusive upper bound). Use ..= to include the upper bound. Here we sum 1 + 2 + 3 + 4 + 5 = 15.

  t = 0;
  for i in 1..6 {
    t += i;
  }
  assert(t == 15, "Total was {t} instead of 15");

rev(range) reverses the direction of any range. 1..=5 is inclusive, so it visits 5, 4, 3, 2, 1 in that order. Multiplying each digit into a running total builds the number 54321.

  t = 0;
  for i in rev(1..=5) {
    t = t * 10 + i;
  }
  assert(t == 54321, "Result was {t} instead of 54321");

Nested loops and break

Loft has no while or loop keyword — all repetition uses for with a range, so there is always a clear upper bound on how many iterations can occur. break exits the nearest enclosing loop immediately. To exit an outer loop from inside an inner one, write outerVar#break. Here x#break leaves both loops when the product x*y reaches 16.

  b = "";
  for x in 1..5 {
    for y in 1..5 {
      if y > x {
        break;

this breaks the inner y loop

      }
      if x * y >= 16 {
        x# break;
      }
      if len(b) > 0 {
        b += "; ";
      }
      b += "{x}:{y}";
    }
  }
  assert(b == "1:1; 2:1; 2:2; 3:1; 3:2; 3:3; 4:1; 4:2; 4:3", "Incorrect sequence '{b}'");

Loop helpers: #first and #count

Inside a loop body you have access to some useful metadata about the current iteration. x#first is true only for the very first element that passes the filter. x#count is a zero-based index counting only the elements that were not filtered out. An if clause directly after in range filters which values enter the loop — here we skip every value where x % 3 == 1, keeping 2, 3, 5, 6, 8, 9.

  b = "";
  for x in 1..=9 if x % 3 != 1 {
    if !x#first {
      b += ", ";
    }
    b += "{x#count}:{x}";
  }
  assert(b == "0:2, 1:3, 2:5, 3:6, 4:8, 5:9", "Sequence '{b}'");

Formatting a loop inline

A for loop can appear inside a format string. The results are collected into a bracketed, comma-separated list. A format specifier after the closing brace is applied to every element — :02 means at least 2 digits, zero-padded. This is handy for building compact representations on the fly.

  assert("a{for x in 1..7 {x*2}:02}b" == "a[02,04,06,08,10,12]b", "Format range");
}