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

Collection literals

A collection literal writes an array, a map or a set in one expression:

trait Shape {
    area(): int;
}

struct Square {
    int side;

    construct(side: int) {
        this.side = side;
    }
}

extend Square with Shape {
    area(): int {
        return side * side;
    }
}

total(values: long[]): long {
    long sum = 0;
    for (var value in values) {
        sum += value;
    }
    return sum;
}

main(): int {
    # An array literal is an Array of its elements' type.
    var primes = [2, 3, 5, 7];
    assert(primes.count == 4 && primes[3] == 7, "an Array<int>");

    # A trailing comma lets a long literal put one element per line.
    var operators = [
        "===",
        "!==",
        "=>",
    ];
    assert(operators[1] == "!==", "an Array<String>");

    # Where an Array type is expected, the elements convert to its element type.
    assert(total([1, 2, 3]) == 6, "an Array<long> argument");
    Shape[] shapes = [Square(2), Square(3)];
    assert(shapes[1].area() == 9, "elements convert to the trait type");

    # A map literal is a HashMap; a repeated key keeps its last value.
    var ages = ["Ada": 36, "Alan": 41];
    assert(ages["Alan"] == 41, "a HashMap<String, int>");

    # An array literal where a HashSet is expected makes a set.
    HashSet<String> keywords = ["if", "else", "if"];
    assert(keywords.count == 2, "duplicates are kept once");

    # [] and [:] are empty, where the type is declared.
    long[] none = [];
    HashMap<String, int> nobody = [:];
    assert(none.is_empty && nobody.count == 0, "empty collections");
    return 0;
}

Arrays

  • [a, b, c] is a new Array<T> holding the elements in order. A trailing comma is allowed: [a, b, c,].
  • With no other type to go by, T is the first element’s type, and every later element converts to it the way an argument converts to a parameter: [1, 2] is an Array<int>, and [1, "a"] is an error.
  • Where an array type is expected (a declared variable, a parameter, a return value, an assignment), T is that type’s element type instead, and every element converts to it: long[] a = [1, 2];, Shape[] s = [square, circle];.
  • Nested literals are typed the same way: long[][] grid = [[1], [2, 3]];.
  • [] is an empty array. It needs a declared type (long[] none = [];), since it has no element to take a type from:
main.pep:2:13: error: empty array literal has no element type; declare the variable's type or write 'Array<T>()'
    2 |     var a = [];
      |             ^~

Maps

  • [k1: v1, k2: v2] is a new HashMap<K, V> holding the entries. K and V come from the first entry, or from the HashMap<K, V> a declared variable, parameter or return type expects.
  • A key written twice keeps its last value, as two insert calls would.
  • [:] is an empty map, and needs a declared type: HashMap<String, int> none = [:];.

Sets

There is no separate set literal. An array literal where a HashSet<K> or a Set<K> is expected converts to one through the set’s implicit constructor, which keeps each element once: HashSet<String> keywords = ["if", "else"];.

Types of specializations

A literal never changes a collection’s type after it is built: Array<int> and Array<long> are different types, and neither converts to the other.

main.pep:2:21: error: cannot initialize variable 'a' of type 'Array<long>' with a value of type 'Array<int>'
    2 |     Array<long> a = Array<int>();
      |                     ^~~~~~~~~~