8.15.0.10

2.5 Repetitions🔗ℹ

As we saw in Lists, the list form supports repetition binding via ..., and it can use a repetition via ... when creating a new list:

> def [x, ...] = [1, 2, 3]

> [x, ..., 100]

[1, 2, 3, 100]

In either bindings or expressions, ... can be nested to create and use repetitions of greater depth. For example, using ... within a term that is already before ... creates a binding of depth 2, which is a repetition of repetitions.

> def [[z, ...], ...] = [[1, 2, 3], [5, 6]]

> [[z, ..., 100], ...]

[[1, 2, 3, 100], [5, 6, 100]]

In this example, both ...s are recognized directly by the list binding form, but the list-construction expression follows a more general rule. The outer ... (i.e., the last one) allows any repetition of depth 1 before it, and [z, ..., 100] creates a repetition of depth 1 by using ... after z, which is a repetition of depth 2.

When a repetition is followed by multiple ...s in a row, as opposed to nested ...s, then the repetitions that would be accessed by nesting are flattened into a single repetition. This flattening has the effect of appending sequences.

> def [[z, ...], ...] = [[1, 2, 3], [5, 6]]

> [z, ..., ...]

[1, 2, 3, 5, 6]

Some other expression-like forms serve as repetition forms when they are used in a repetition position. For example, an operator that is defined by operator forms a repetition when it has repetition arguments, so a negation term -x creates a repetition of depth 1 when x is a repetition of depth 1:

> def [x, ...] = [1, 2, 3]

> [-x, ...]

[-1, -2, -3]

When a repetition is built from multiple other repetitions, the repetitions are used in parallel. For example, + used on two repetitions maps addition over the repetitions. The two repetitions must have the same length.

> def [x, ...] = [1, 2, 3]

> def [y, ...] = [4, 5, 6]

> [x+y, ...]

[5, 7, 9]

When repetitions of different depths are combined, the shallower repetition is repeated for outer layers of the deeper repetition.

> def [[z, ...], ...] = [[1, 2], [3, 4], [5, 6]]

> def [x, ...] = ["a", "b"]

> [[x +& z, ...], ...]

[["a1", "b2"], ["a3", "b4"], ["a5", "b6"]]

A literal value or a variable works as a repetition of depth 0. A repetition of depth 0 is not useful in itself, but it’s useful in combination with a repetition of greater depth. For example, using 1 as a repetition lets us add it to every element of a repetition.

> def [x, ...] = [1, 2, 3]

> [x+1, ...]

[2, 3, 4]

> def five = 5

> [x+five, ...]

[6, 7, 8]

> [five, ...]

five: used with wrong repetition depth

  expected: 0

  actual: 1

Analogous to lists, map and set constructions work as repetition forms.

> def [x, ...] = [1, 2, 3]

> [{x}, ...]

[{1}, {2}, {3}]

> [{x: #true}, ...]

[{1: #true}, {2: #true}, {3: #true}]

Function calls, the . operator, array or map access via [], and syntax templates all work as repetition forms, too, given other repetitions to start with.

> class Posn(x, y)

> fun mirror(Posn(x, y)) :~ List.of(Posn):

    [Posn(x, y), Posn(y, x)]

> def [p, ...] = [Posn(1, 2), Posn(3, 4), Posn(5, 6)]

> ['z + $(mirror(p)[1].x)', ...]

['z + 2', 'z + 4', 'z + 6']