On this page:
...
...
&
&
~&
~&
index
index
deepen
deepen
each
each
8.90

8.7 Repetitions🔗ℹ

A repetition represents a sequence of values, and it can be used in designated repetition positions (in much the same way that expressions appear in expression positions and bindings in binding positions). For example, a repetition can be used at the end of a list with ... after it.

One way to create a repetition is through a binding that uses .... Some expression-like forms are also repetition forms, creating new repetitions from one or more repetitions. For example, if x is bound as a repetition, then x+1 can be used as a repetition to add 1 to each element of x:

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

> [x+1, ...]

[2, 3, 4]

When + is used as a repetition operator, then it expects a repetition for both arguments, but a literal value like 1 works as a repetition via #%literal.

A repetition has a depth, and each repetition context expects a repetition of a particular depth, typically based on how many ...s appear after the repetition context. In the above example, x is bound as a repetition of depth 1, and it is used in a context of depth 1, since there is one ... after x+1 to form the list. Using multiple ... in a binding context typically binds at a greater depth, as in this example that binds and uses z at depth 2:

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

> [[z+1, ...], ...]

[[2, 3, 4], [5, 6]]

More precisely, in this example, the outer list construction expects a repetition of depth 1 before its ..., and [z+1, ...] creates a repetition of depth 1. The [z+1, ...] repetition is depth 1, because a list repetition subtracts one from the depth of a repetition before (another) ..., and z+1 has depth 2 due to the z binding.

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], [4, 5]]

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

[1, 2, 3, 4, 5]

When a repetition form combines multiple repetitions, then unless documented otherwise, elements at the same repetition depth are drawn from the repetitions in parallel.

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

> def [y, ...] = ["a", "b", "c"]

> [[x, y], ...]

[[1, "a"], [2, "b"], [3, "c"]]

When combined repetitions are at different depths, the shallower repetition is repeated for outer layers of the deeper repetition. That’s why the x+1 and z+1 examples above work: a literal 1 works as repetition of depth 0, but it is repeated as needed to match the x repetition of depth 1 or the z repetition of depth 2. A repetition of depth 2 can be similarly repeated to match a repetition of depth 2:

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

> def [y, ...] = [10, 100, 1000]

> [[z+y, ...], ...]

[[11, 102, 1003], [14, 105, 1006]]

In other words, unless otherwise documented, the depth of a repetition formed by combining repetitions is the maximum of the depths of the combined repetitions, so z+y is a repetition of depth 2. See also deepen for more explicit control over repetition deepening.

Expressions with side effects or short-circuiting operators can appear within a repetition. Each effect and control-flow choice is applied on demand per element of the repetition, which can be different than constructing an intermediate repetition.

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

> [x > 1 && println(x), ...]

2

3

[#false, #void, #void]

> block:

    let [void, ...] = [println(x), ...]

    [x > 1 && void, ...]

1

2

3

[#false, #void, #void]

When an identifier is bound as a repetition, it is bound in the repet space, but also in the expr. The expr binding reports an error, but the intent of the binding is to shadow any existing expression binding for the identifier.

binding operator

...

 

expression

...

The ... “binding” form or ... “expression” form is not really allowed as an binding or expression, but it can appear in places where an binding or expression would otherwise be allowed.

In a binding-like position, ... tends to change a nearby binding into a repetition binding. For example, in fun (x, ...): body, the ... causes x to be bound to a repetition.

In an expression-like position, ... tends to change a nearby expression position into a repetition position, which is a place where a repetition binding or operator can be used. For example, the list expression [x, ...] has x in a repetition position, which would make sense as part of the body of fun (x, ...): body, since x is bound there as a repetition.

Function arguments, list patterns, and syntax patterns are among the places that recognize ... to create repetition bindings. Function calls, list constructs, and syntax templates are among the places that recognize ... to use repetition positions.

expression

& list_expr

 

binding operator

& list_bind

Provided both normally and as meta.

The & expression operator and binding operator can only be used in places where it’s specifically recognized, normally either to reference or bind the “rest” of a data structure. The List constructor, Map constructor, fun form, and the #%call form are among the places that recognize &.

> def [a, b, & others] = [1, 2, 3, 4]

> others

[3, 4]

> [0, & others]

[0, 3, 4]

expression

~& map_expr

 

binding operator

~& map_bind

Provided both normally and as meta.

The ~& expression operator and binding operator can only be used in places where it’s specifically recognized, normally to bind the “rest” of a map with keywords as keys. The fun and #%call forms are among the places that recognize ~&.

> fun roster(~manager: who, ~& players):

    players

> roster(~pitcher: "Dave", ~manager: "Phil", ~catcher: "Johnny")

{#'~catcher: "Johnny", #'~pitcher: "Dave"}

repetition

index(repet)

 

repetition

index(repet, depth_int)

 

expression

index

Creates a repetition with the same depth and count (at each nesting depth) as repet, but where the repetition elements are integers counting up from 0.

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

> [index(x), ...]

[0, 1, 2]

> {x: index(x), ...}

{"a": 0, "b": 1, "c": 2}

If depth_int is provided, it must be a literal positive integer, and the depth of repet must be at least depth_int. The elements of the generated repetition correspond to the repetition at level nested depth depth_int counting from the innermost nesting. Omitting depth_int is the same as supplying 1.

> def [[y, ...], ...] = [["a", "b", "c"], ["d", "e"]]

> [[index(y), ...], ...]

[[0, 1, 2], [0, 1]]

> [[index(y, 2), ...], ...]

[[0, 0, 0], [1, 1]]

> [index([y, ...]), ...]

[0, 1]

The index expression form is always an error. It is intended to provide a clearer message when index was meant to be used in a repetition context.

repetition

deepen shallow_repet ~like deep_repet

 

repetition

deepen shallow_repet ~like_inner deep_repet

 

expression

deepen

Creates a repetition that has the elements of shallow_repet, but the depth of deep_repet. The shallow_repet is deepened by repeating elements in a way that depends on whether ~like or ~like_inner is used:

  • With ~like, the overall repetition shallow_repet is repeated as many times as needed to match the outermost layers of deep_repet. This is the same kind of deepening that is performed automatically when repetitions of different depths are under a shared ....

  • With ~like_inner, the repetition shallow_repet is expected to match the outermost repetitions of deep_repet in lengths, and the innermost element of shallow_repet is repeated for each further inner repetition layer of deep_repet. If the outermost repetition counts of shallow_repet and deep_repet do not match, a mismatch exception is thrown when the repetition is used.

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

> [deepen 0 ~like x, ...]

[0, 0]

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

> [[deepen 0 ~like y, ...], ...]

[[0, 0, 0], [0, 0]]

> [[deepen x ~like y, ...], ...]

[["a", "b"], ["a", "b"]]

> [[deepen x ~like_inner y, ...], ...]

[["a", "a", "a"], ["b", "b"]]

> [[{deepen x ~like_inner y, y}, ...], ...]

[[{1, "a"}, {2, "a"}, {3, "a"}], [{4, "b"}, {5, "b"}]]

The deepen expression form is always an error. It is intended to provide a clearer message when deepen was meant to be used in a repetition context.

repetition

each seq_expr

 

expression

each

Creates a repetition from an expression that produces a sequence.

In the case of something like a list, each could be considered a shorthand for using ... with a List binding.

> def lst = [1, 2, 3]

> block:

    let List[x, ...] = lst

    [x+1, ...]

[2, 3, 4]

> [(each lst) + 1, ...]

[2, 3, 4]

The each expression form is always an error. It is intended to provide a clearer message when each was meant to be used in a repetition context.

The each repetion form is different from the each form for for. They use the same name, because they are both related to extracting elements from a sequence.