Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Arrays, Ranges, and Iteration

Ferlium provides compact tools for working with sequences of values: arrays, ranges, and for loops. This chapter introduces how to create and read arrays, describe numeric sequences with ranges, and iterate over both forms in an expression-oriented style.

Arrays

Arrays store ordered values of a single element type.

Array literals

Array literals use square brackets:

let a = [1, 2, 3];
let b = [true, false, true];
let c = ["a", "b"];

A trailing comma is allowed:

let xs = [1, 2, 3,];

[] is valid syntax, but by itself its element type is unknown, so it needs context:

let empty: [int] = [];

Indexing

Use array[index] to access elements:

let xs = [10, 20, 30];
let first = xs[0];
let second = xs[1];

Use indexed assignment to update an element through a mutable array binding:

let mut xs = [10, 20, 30];
xs[1] = 25;
xs

Negative indices count from the end:

let xs = [10, 20, 30];
let last = xs[-1];
let before_last = xs[-2];

Indexing out of bounds is a runtime error.

Arrays are values with one element type

Arrays are regular values: you can bind them, pass them around, and return them from expressions.

let xs = [1, 2, 3];
let ys = xs;
ys[0]

All elements must have the same type. Mixing multiple element types is a type error:

[1, true]   // type error

Element type inference

Ferlium infers the element type from array contents:

let ints = [1, 2, 3];      // inferred as [int]
let floats = [1.0, 2.5];   // inferred as [float]

For an empty array, add context with an annotation:

let mut out: [int] = [];

Ranges

Ranges are a compact way to describe integer sequences.

Exclusive and inclusive ranges

Use start..end for an exclusive upper bound:

let r = 1..4;   // 1, 2, 3

Use start..=end for an inclusive upper bound:

let r = 1..=4;  // 1, 2, 3, 4

Ranges also work in downward direction:

let r = 5..2;   // 5, 4, 3
let r = 5..=2;  // 5, 4, 3, 2

Parenthesize computed bounds:

 let start = 0;
let r = start..(start + 3);

Iteration with for

for loops iterate over a sequence and execute a body for each element.

Iterating over ranges and arrays

Iteration works the same way for ranges and arrays:

for i in 0..3 { /* ... */ };
for x in [10, 20, 30] { /* ... */ };

Destructuring in for loops

The loop variable can destructure tuples and records:

for (i, name) in [(0, "zero"), (1, "one"), (2, "two")] {
    let entry = f"{i} = {name}";
};

Accumulating with let mut

A common pattern is to keep mutable state outside the loop and update it inside:

let mut sum = 0;
for i in 1..=4 {
    sum += i;
};
sum

Collecting values works the same way:

let mut out: [int] = [];
for i in 2..5 {
    array_append(out, i);
};
out

For more on in-place array and string construction, see Sequence Processing.

Loop variable scope and expression result

The loop variable is local to the loop body. The for expression itself evaluates to ().

let mut count = 0;
for n in [1, 2, 3] {
    count += 1;
};
count

For unconditional loops and labeled break or continue, see Control Flow and Pattern Matching.

What comes next

The next chapter introduces user-defined types, so you can give names to your own product and sum types instead of relying only on arrays, tuples, and records.