Skip to Content

List

list<T> is a growable sequence and the workhorse container. Its length grows and shrinks with additions and removals, and elements can be read and written by index.

The lowercase list is a built-in name, usable without any import. The standard library declares the capitalized List<T> (inside the std::Collections module).

Creation

Two ways in: a constructor plus Add, or a collection literal constructed to the target type:

val xs = new list<int>(); xs.Add(3); xs.Add(1); xs.Add(2); print(xs, xs.Count); val ys: list<int> = [4, 5];
[3, 1, 2] 3
Note

A collection literal with no annotation is inferred as an array by default; to get a list you must annotate list<T> explicitly.

Add, remove, update

xs.Insert(1, 99); print(xs); xs.Set(0, 30); xs[1] = 10; print(xs); xs.Remove(30); xs.RemoveAt(0); print(xs);
[3, 99, 1, 2] [30, 10, 1, 2] [1, 2]

Insert(index, value) inserts at an index; Set(index, value) is equivalent to the indexer write xs[index] = value; Remove(value) deletes the first match, RemoveAt(index) deletes by position. Use AddRange(other) for bulk appends and Clear() to empty the list.

Reading and lookup

print(xs[1], xs.Contains(2), xs.IndexOf(2)); print(try? xs.Get(9));
2 True 1 null
Note

IndexOf returns -1 on a miss.

Containers expose two ways to read a value:

xs[1] // assertion form: out of range crashes outright; use it when you are sure the index exists try xs.Get(9) // fallible form: out of range raises IndexOutOfRange; must be consumed with a try form try? xs.Get(9) // swallow the failure → null try? xs.Get(9) ?? -1 // or handle the null directly and land on a default

Standard combinations:

val hp = try? enemies.Get(i)?.Hp ?? 0; // safe read past the end if try? items.Get(k) is Item it { use(it); } // is-type check + narrowing val name = try? names.Get(0) ?? "?"; // ?? as the fallback

Higher-order methods

Search, transformation, and aggregation all have ready-made methods that take a lambda:

xs.Insert(0, 30); xs.Insert(1, 10); print(xs); print(xs.Where(n => n > 5), xs.Map(n => n * 2)); print(xs.Filter(n => n % 2 == 0), xs.Reduce((a, b) => a + b, 0)); print(xs.Min(), xs.Max(), xs.Sum()); print(xs.Any(n => n > 100), xs.All(n => n > 0)); print(try? xs.First(n => n > 50));
[30, 10, 1, 2] [30, 10] [60, 20, 2, 4] [30, 10, 2] 43 1 30 43 False True null
  • Where and Filter are synonyms; both return a new list.
  • Map transforms element by element; Reduce folds with a seed.
  • First(pred) fails with SeqEmpty on no match.
  • FindIndex(pred) and LastIndexOf(v) each handle one kind of search (both -1 on a miss).

The idiom for taking the head of a possibly empty list:

val singles: list<int> = [7]; print(try? singles.First() ?? 0); val empty = new list<int>(); print(try? empty.First() ?? 0, try? empty.Last() ?? -1);
7 0 -1

Sorting and rearranging

print(xs.Sorted(), xs.Reversed(), xs.Distinct()); print(xs.Take(2), xs.Skip(1));
[1, 2, 10, 30] [2, 1, 10, 30] [30, 10, 1, 2] [30, 10] [10, 1, 2]

Sorted() / Reversed() / Distinct() / Take(n) / Skip(n) all return new lists and leave the original untouched. To sort in place use Sort(), to reverse in place use Reverse().

Joining, zipping, and conversion

print(xs.Join("-")); val zs: list<int> = [7]; print(xs.Zip(zs)); print(xs.ToArray().Length);
30-10-1-2 [(30, 7)] 4

Join(separator) glues the elements into a string; Zip(other) pairs two lists position-wise into a list of tuples, with the result as long as the shorter side; ToList() copies into a fresh list, ToArray() converts to a fixed-length array, and ToSet() converts to a set, deduplicating.

Notes

  • A printed list is just square brackets and commas: [1, 2, 3].
  • for x in xs iterates the elements directly; see the iterator protocol.
  • Element equality and container equality (value semantics compare by value, reference semantics by identity) are covered in equality and hashing.
Last updated on October 11, 2026