# Variadics

<!--
Part of the Carbon Language project, under the Apache License v2.0 with LLVM
Exceptions. See /LICENSE for license information.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-->

<!-- toc -->

## Table of contents

-   [Basics](#basics)
    -   [Overview](#overview)
    -   [Packs and each-names](#packs-and-each-names)
    -   [Pack expansions](#pack-expansions)
        -   [Pack expansion expressions and statements](#pack-expansion-expressions-and-statements)
        -   [Pack expansion patterns](#pack-expansion-patterns)
    -   [Additional examples](#additional-examples)
-   [Execution Semantics](#execution-semantics)
    -   [Expressions and statements](#expressions-and-statements)
    -   [Pattern matching](#pattern-matching)
-   [Typechecking](#typechecking)
    -   [Tuples, packs, segments, and shapes](#tuples-packs-segments-and-shapes)
    -   [Iterative typechecking of pack expansions](#iterative-typechecking-of-pack-expansions)
    -   [Typechecking patterns](#typechecking-patterns)
    -   [Typechecking pattern matches](#typechecking-pattern-matches)
-   [Appendix: Type system formalism](#appendix-type-system-formalism)
    -   [Explicit deduced arities](#explicit-deduced-arities)
    -   [Typing and shaping rules](#typing-and-shaping-rules)
    -   [Reduction rules](#reduction-rules)
    -   [Equivalence, equality, and convertibility](#equivalence-equality-and-convertibility)
    -   [Pattern match typechecking algorithm](#pattern-match-typechecking-algorithm)
        -   [Canonicalization algorithm](#canonicalization-algorithm)
-   [Alternatives considered](#alternatives-considered)
-   [References](#references)

<!-- tocstop -->

## Basics

### Overview

A "pack expansion" is a syntactic unit beginning with `...`, which is a kind of
compile-time loop over sequences called "packs". Packs are initialized and
referred to using "each-names", which are marked with the `each` keyword at the
point of declaration and the point of use.

The syntax and behavior of a pack expansion depends on its context, and in some
cases on a keyword following the `...`:

-   In a tuple literal expression (such as a function call argument list), `...`
    iteratively evaluates its operand expression, and treats the values as
    successive elements of the tuple.
-   `...and` and `...or` iteratively evaluate a boolean expression, combining
    the values using `and` and `or`. Normal short-circuiting behavior for the
    resulting `and` and `or` operators applies at runtime.
-   In a statement context, `...` iteratively executes a statement.
-   In a tuple literal pattern (such as a function parameter list), `...`
    iteratively matches the elements of the scrutinee tuple. In conjunction with
    pack bindings, this enables functions to take an arbitrary number of
    arguments.

This example illustrates many of the key concepts:

```carbon
// Takes an arbitrary number of vectors with arbitrary element types, and
// returns a vector of tuples where the i'th element of the vector is
// a tuple of the i'th elements of the input vectors.
fn Zip[... each ElementType:! type]
      (... each vector: Vector(each ElementType))
      -> Vector((... each ElementType)) {
  ... var each iter: auto = each vector.Begin();
  var result: Vector((... each ElementType));
  while (...and each iter != each vector.End()) {
    result.push_back((... each iter));
    ... each iter++;
  }
  return result;
}
```

### Packs and each-names

A _pack_ is a sequence of a fixed number of values called "elements", which may
be of different types. Packs are very similar to tuple values in many ways, but
they are not first-class values -- in particular, no run-time expression
evaluates to a pack. The _arity_ of a pack is a compile-time value representing
the number of values in the sequence.

An _each-name_ consists of the keyword `each` followed by the name of a pack,
and can only occur inside a pack expansion. On the Nth iteration of the pack
expansion, an each-name refers to the Nth element of the named pack. As a
result, a binding pattern with an each-name, such as `each ElementType:! type`,
acts as a declaration of all the elements of the named pack, and thereby
implicitly acts as a declaration of the pack itself.

Note that `each` is part of the name syntax, not an expression operator, so it
binds more tightly than any expression syntax. For example, the loop condition
`...and each iter != each vector.End()` in the implementation of `Zip` is
equivalent to `...and (each iter) != (each vector).End()`.

### Pack expansions

A _pack expansion_ is an instance of one of the following syntactic forms:

-   A statement of the form "`...` _statement_".
-   A tuple expression element of the form "`...` _expression_", with the same
    precedence as `,`.
-   A tuple pattern element of the form "`...` _pattern_", with the same
    precedence as `,`.
-   An implicit parameter list element of the form "`...` _pattern_", with the
    same precedence as `,`.
-   An expression of the form "`...` `and` _expression_" or "`...` `or`
    _expression_", with the same precedence as `and` and `or`.

The statement, expression, or pattern following the `...` (and the `and`/`or`,
if present) is called the _body_ of the expansion.

The `...` token can also occur in a tuple expression element of the form "`...`
`expand` _expression_", with the same precedence as `,`. However, that syntax is
not considered a pack expansion, and has its own semantics: _expression_ must
have a tuple type, and "`...` `expand` _expression_" evaluates _expression_ and
treats its elements as elements of the enclosing tuple literal. This is
especially useful for using non-literal tuple values as function call arguments:

```carbon
fn F(x: i32, y: String);
fn MakeArgs() -> (i32, String);

F(...expand MakeArgs());
```

`...and`, `...or`, and `...expand` can be trivially distinguished with one token
of lookahead, and the other meanings of `...` can be distinguished from each
other by the context they appear in. As a corollary, if the nearest enclosing
delimiters around a `...` are parentheses, they will be interpreted as forming a
tuple rather than as grouping. Thus, expressions like `(... each ElementType)`
in the above example are tuple literals, even though they don't contain commas.

By convention, `...` is always followed by whitespace, except that `...and`,
`...or`, and `...expand` are written with no whitespace between the two tokens.
This serves to emphasize that the keyword is not part of the expansion body, but
rather a modifier on the syntax and semantics of `...`.

All each-names in a given expansion must refer to packs with the same arity,
which we will also refer to as the arity of the expansion. If an expansion
contains no each-names, it must be a pattern, or an expression in the type
position of a binding pattern, and its arity is deduced from the scrutinee.

A pack expansion or `...expand` expression cannot contain another pack expansion
or `...expand` expression.

An each-name cannot be used in the same pack expansion that declares it. In most
if not all cases, an each-name that violates this rule can be changed to an
ordinary name, because each-names are only necessary when you need to transfer a
pack from one pack expansion to another.

#### Pack expansion expressions and statements

A pack expansion expression or statement can be thought of as a kind of loop
that executes at compile time (specifically, monomorphization time), where the
expansion body is implicitly parameterized by an integer value called the _pack
index_, which ranges from 0 to one less than the arity of the expansion. The
pack index is implicitly used as an index into the packs referred to by
each-names. This is easiest to see with statement pack expansions. For example,
if `a`, `x`, and `y` are packs with arity 3, then
`... each a += each x * each y;` is roughly equivalent to

```carbon
a[:0:] += x[:0:] * y[:0:];
a[:1:] += x[:1:] * y[:1:];
a[:2:] += x[:2:] * y[:2:];
```

Here we are using `[:N:]` as a hypothetical pack indexing operator for purposes
of illustration; packs cannot actually be indexed in Carbon code.

> **Future work:** We're open to eventually adding indexing of variadics, but
> that remains future work and will need its own proposal.

`...and` and `...or` behave like chains of the corresponding boolean operator,
so `...and F(each x, each y)` behaves like
`true and F(x[:0:], y[:0:]) and F(x[:1:], y[:1:]) and F(x[:2:], y[:2:])`. They
can also be interpreted as looping constructs, although the rewrite is less
straightforward because Carbon doesn't have a way to write a loop in an
expression context. An expression like `...and F(each x, each y)` can be thought
of as evaluating to the value of `result` after executing the following code
fragment:

```
var result: bool = true;
for (let i:! i32 in (0, 1, 2)) {
  result = result && F(x[:i:], y[:i:]);
  if (result == false) { break; }
}
```

`...` in a tuple literal behaves like a series of comma-separated tuple
elements, so `(... F(each x, each y))` is equivalent to
`(F(x[:0:], y[:0:]), F(x[:1:], y[:1:]), F(x[:2:], y[:2:]))`. This can't be
expressed as a loop in Carbon code, but it is still fundamentally iterative.

#### Pack expansion patterns

A pack expansion pattern "`...` _subpattern_" appears as part of a tuple pattern
(or an implicit parameter list), and matches a sequence of tuple elements if
each element matches _subpattern_. For example, in the signature of `Zip` shown
earlier, the parameter list consists of a single pack expansion pattern
`... each vector: Vector(each ElementType)`, and so the entire argument list
will be matched against the binding pattern
`each vector: Vector(each ElementType)`.

Since _subpattern_ will be matched against multiple scrutinees (or none) in a
single pattern-matching operation, a binding pattern within a pack expansion
pattern must declare an each-name (such as `each vector` in the `Zip` example),
and the Nth iteration of the pack expansion will initialize the Nth element of
the named pack from the Nth scrutinee. The binding pattern's type expression may
contain an each-name (such as `each ElementType` in the `Zip` example), but if
so, it must be a deduced parameter of the enclosing pattern.

> **Future work:** That restriction can probably be relaxed, but we currently
> don't have motivating use cases to constrain the design.

### Additional examples

```carbon
// Computes the sum of its arguments, which are i64s
fn SumInts(... each param: i64) -> i64 {
  var sum: i64 = 0;
  ... sum += each param;
  return sum;
}
```

```carbon
// Concatenates its arguments, which are all convertible to String
fn StrCat[... each T:! ConvertibleToString](... each param: each T) -> String {
  var len: i64 = 0;
  ... len += each param.Length();
  var result: String = "";
  result.Reserve(len);
  ... result.Append(each param.ToString());
  return result;
}
```

```carbon
// Returns the minimum of its arguments, which must all have the same type T.
fn Min[T:! Comparable & Value](first: T, ... each next: T) -> T {
  var result: T = first;
  ... if (each next < result) {
    result = each next;
  }
  return result;
}
```

```carbon
// Invokes f, with the tuple `args` as its arguments.
fn Apply[... each T:! type, F:! CallableWith(... each T)]
    (f: F, args: (... each T)) -> auto {
  return f(...expand args);
}
```

```carbon
// Toy example of mixing variadic and non-variadic parameters.
// Takes an i64, any number of f64s, and then another i64.
fn MiddleVariadic(first: i64, ... each middle: f64, last: i64);
```

```carbon
// Toy example of using the result of variadic type deduction.
fn TupleConcat[... each T1: type, ... each T2: type](
    t1: (... each T1), t2: (... each T2)) -> (... each T1, ... each T2) {
  return (...expand t1, ...expand t2);
}
```

## Execution Semantics

### Expressions and statements

In all of the following, N is the arity of the pack expansion being discussed,
and `$I` is a notional variable representing the pack index. These semantics are
implemented at monomorphization time, so the value of N is a known integer
constant. Although the value of `$I` can vary during execution, it is
nevertheless treated as a constant.

A statement of the form "`...` _statement_" is evaluated by executing
_statement_ N times, with `$I` ranging from 0 to N - 1.

An expression of the form "`...and` _expression_" is evaluated as follows: a
notional `bool` variable `$R` is initialized to `true`, and then "`$R = $R and`
_expression_" is executed up to N times, with `$I` ranging from 0 to N - 1. If
at any point `$R` becomes false, this iteration is terminated early. The final
value of `$R` is the value of the expression.

An expression of the form "`...or` _expression_" is evaluated the same way, but
with `or` in place of `and`, and `true` and `false` transposed.

A tuple expression element of the form "`...` _expression_" evaluates to a
sequence of N values, where the k'th value is the value of _operand_ where `$I`
is equal to k - 1.

An each-name evaluates to the `$I`th value of the pack it refers to (indexed
from zero).

### Pattern matching

The semantics of pack expansion patterns are chosen to follow the general
principle that pattern matching is the inverse of expression evaluation, so for
example if the pattern `(... each x: auto)` matches some scrutinee value `s`,
the expression `(... each x)` should be equal to `s`. These semantics are
implemented at monomorphization time, so all types are known constants, and all
all arities are known.

A tuple pattern can contain no more than one subpattern of the form "`...`
_operand_". When such a subpattern is present, the N elements of the pattern
before the `...` expansion are matched with the first N elements of the
scrutinee, and the M elements of the pattern after the `...` expansion are
matched with the last M elements of the scrutinee. If the scrutinee does not
have at least N + M elements, the pattern does not match.

The remaining elements of the scrutinee are iteratively matched against
_operand_, in order. In each iteration, `$I` is equal to the index of the
scrutinee element being matched, minus N.

On the Nth iteration, a binding pattern binds the Nth element of the named pack
to the Nth scrutinee value.

## Typechecking

### Tuples, packs, segments, and shapes

In order to discuss the underlying type system for variadics, we will need to
introduce some pseudo-syntax to represent values and expressions that occur in
the type system, but cannot be expressed directly in user code. We will use
non-ASCII glyphs such as `«»‖⟬⟭` for that pseudo-syntax, to distinguish it from
valid Carbon syntax.

In the context of variadics, we will say that a tuple literal consists of a
comma-separated sequence of _segments_, and reserve the term "elements" for the
components of a tuple literal after pack expansion. For example, the expression
`(... each foo)` may evaluate to a tuple value with any number of elements, but
the expression itself has exactly one segment.

Each segment has a type, which expresses (potentially symbolically) both the
types of the elements of the segment and the arity of the segment. The type of a
tuple literal is a tuple literal of the types of its segments. For example,
suppose we are trying to find the type of `z` in this code:

```carbon
fn F[... each T:! type]((... each x: Optional(each T)), (... each y: i32)) {
  let z: auto = (0 as f32, ... each x, ... each y);
}
```

We proceed by finding the type of each segment. The type of `0 as f32` is `f32`,
by the usual non-variadic typing rules. The type of `... each x` is
`... Optional(each T)`, because `Optional(each T)` is the declared type of
`each x`, and the type of a pack expansion is a pack expansion of the type of
its body.

The type of `... each y` is more complicated. Conceptually, it consists of some
number of repetitions of `i32`. We don't know exactly how many repetitions,
because it's implicitly specified by the caller: it's the arity of the second
argument tuple. Effectively, that arity acts as a hidden deduced parameter of
`F`.

So to represent this type, we need two new pseudo-syntaxes:

-   `‖each X‖` refers to the deduced arity of the pack expansion that contains
    the declaration of `each X`.
-   `«E; N»` evaluates to `N` repetitions of `E`. This is called a _arity
    coercion_, because it coerces the expression `E` to have arity `N`. `E` must
    not contain any pack expansions, each-names, or pack literals (see below).

Combining the two, the type of `... each y` is `... «i32; ‖each y‖»`. Thus, the
type of `z` is `(f32, ... Optional(each T), ... «i32; ‖each y‖»)`.

Now, consider a modified version of that example:

```carbon
fn F[... each T:! type]((... each x: Optional(each T)), (... each y: i32)) {
  let (... each z: auto) = (0 as f32, ... each x, ... each y);
}
```

`each z` is a pack, but it has the same elements as the tuple `z` in our earlier
example, so we represent its type in the same way, as a sequence of segments:
`⟬f32, Optional(each T), «i32; ‖each y‖»⟭`. The `⟬⟭` delimiters make this a
_pack literal_ rather than a tuple literal. Notice one subtle difference: the
segments of a pack literal do not contain `...`. In effect, every segment of a
pack literal acts as a separate loop body. As with the tuple literal syntax, the
pack literal pseudo-syntax can also be used in patterns.

The _shape_ of a pack literal is a tuple of the arities of its segments, so the
shape of `⟬f32, Optional(each T), «i32; ‖each y‖»⟭` is
`(1, ‖each T‖, ‖each y‖)`. Other expressions and patterns also have shapes. In
particular, the shape of an arity coercion `«E; A»` is `(A,)`, the shape of
`each X` is `(‖each X‖,)`, and the shape of an expression that does not contain
pack literals, shape coercions, or each-names is `(1,)`. The arity of an
expression is the sum of the elements of its shape. See the
[appendix](#typing-and-shaping-rules) for the full rules for determining the
shape of an expression.

If a pack literal is part of some enclosing expression that doesn't contain
`...`, it can be _expanded_, which moves the outer expression inside the pack
literal. For example, `... Optional(⟬each X, Y⟭)` is equivalent to
`... ⟬Optional(each X), Optional(Y)⟭`. Similarly, an arity coercion can be
expanded so long as the parent node is not `...`, a pattern, or a pack literal.
See the [appendix](#reduction-rules) for the full rules governing this
operation. _Fully expanding_ an expression or pattern that does not contain a
pack expansion means repeatedly expanding any pack literals and arity coercions
within it, until they cannot be expanded any further.

The _scalar components_ of a fully-expanded expression `E` are a set, defined as
follows:

-   If `E` is a pack literal, its scalar components are the union of the scalar
    components of the segments.
-   If `E` is an arity coercion `«F; S»`, the only scalar component of `E` is
    `F`.
-   Otherwise, the only scalar component of `E` is `E`.

The scalar components of any other expression that does not contain `...` are
the scalar components of its fully expanded form.

By construction, a segment of a pack literal never has more than one scalar
component. Also by construction, a scalar component cannot contain a pack
literal, pack expansion, or arity coercion, but it can contain each-names, so we
can operate on it using the ordinary rules of non-variadic expressions so long
as we treat the names as opaque.

### Iterative typechecking of pack expansions

Since the execution semantics of an expansion are defined in terms of a notional
rewritten form where we simultaneously iterate over each-names, in principle we
can typecheck the expansion by typechecking the rewritten form. However, the
rewritten form usually would not typecheck as ordinary Carbon code, because the
each-names can have different types on different iterations. Furthermore, the
difference in types can propagate through expressions: if `each x` and `each y`
can have different types on different iterations, then so can `each x * each y`.
In effect, we have to typecheck the loop body separately for each iteration.

However, at typechecking time we usually don't even know how many iterations
there will be, much less what type an each-name will have on any particular
iteration, because the types of the each-names are packs, which are sequences of
segments, not sequences of elements. To solve that problem, we require that the
types of all each-names in a pack expansion must have the same shape. This
enables us to typecheck the pack expansion by simultaneously iterating over
segments instead of input elements.

As a result, the type of an expression or pattern within a pack expansion is a
sequence of segments, or in other words a _pack_, representing the types it
takes on over the course of the iteration. Note, however, that even though such
an expression has a pack type, it does not evaluate to a pack value. Rather, it
evaluates to a sequence of non-pack values over the course of the pack expansion
loop, and its pack type summarizes the types of that sequence.

Within a given iteration, typechecking follows the usual rules of non-variadic
typechecking, except that when we need the type of an each-name, we use the
scalar component of the current segment of its type. As noted above, we can
operate on a scalar component using the ordinary rules of non-variadic
typechecking.

Once the body of a pack expansion has been typechecked, typechecking the
expansion itself is relatively straightforward:

-   A statement pack expansion requires no further typechecking, because
    statements don't have types.
-   An `...and` or `...or` expression has type `bool`, and every segment of the
    operand's type pack must have a type that's convertible to `bool`.
-   For a `...` tuple element expression or pattern, the segments of the
    operand's type pack become segments of the type of the enclosing tuple.

> **TODO:** Discuss typechecking `...expand`.

### Typechecking patterns

A pack expansion pattern has _fixed arity_ if it contains at least one usage of
an each-name that is not a parameter of the enclosing
[full pattern](pattern_matching.md). Otherwise it has _deduced arity_. A tuple
pattern can have at most one segment with deduced arity. For example:

```carbon
class C(... each T:! type) {
  fn F[... each U:! type](... each t: each T, ... each u: each U);
}
```

In the signature of `F`, `... each t: each T` has fixed arity, since the arity
is determined by the arguments passed to `C`, before the call to `F`. On the
other hand, `... each u: each U` has deduced arity, because the arity of
`each U` is determined by the arguments passed to `F`.

After typechecking a full pattern, we attempt to merge as many tuple segments as
possible, in order to simplify the subsequent pattern matching. For example,
consider the following function declaration:

```carbon
fn Min[T:! type](first: T, ... each next: T) -> T;
```

During typechecking, we rewrite that function signature so that it only has one
parameter:

```carbon
fn Min[T:! type](... each args: «T; ‖each next‖+1») -> T;
```

(We represent the arity as `‖each next‖+1` to capture the fact that `each args`
must match at least one element.)

When the pattern is heterogeneous, the merging process may be more complex. For
example:

```carbon
fn ZipAtLeastOne[First:! type, ... each Next:! type]
    (first: Vector(First), ... each next: Vector(each Next))
    -> Vector((First, ... each Next));
```

During typechecking, we transform that function signature to the following form:

```carbon
fn ZipAtLeastOne[... ⟬First, each Next⟭:! «type; ‖each next‖+1»]
    (... each __args: Vector(⟬First, each Next⟭))
    -> Vector((... ⟬First, each Next⟭));
```

We can then rewrite that by replacing the pack of names `⟬First, each Next⟭`
with an invented name `each __Args`, so that the function has only one
parameter:

```carbon
fn ZipAtLeastOne[... each __Args:! «type; ‖each next‖+1»]
    (... each __args: Vector(each __Args))
    -> Vector((... each __Args));
```

We can replace a name pack with an invented each-name only if all of the
following conditions hold:

-   The name pack doesn't use any name more than once. For example, we can't
    apply this rewrite to `⟬X, each Y, X⟭`.
-   The name pack contains exactly one each-name. For example, we can't apply
    this rewrite to `⟬X, Y⟭`.
-   The replacement removes all usages of the constituent names, including their
    declarations. For example, we can't apply this rewrite to `⟬X, each Y⟭` in
    this code, because the resulting signature would have return type `X` but no
    declaration of `X`:
    ```carbon
    fn F[... ⟬X, each Y⟭:! «type; ‖each next‖+1»]
        (... each __args: each ⟬X, each Y⟭) -> X;
    ```
-   The pack expansions being rewritten do not contain any pack literals other
    than the name pack being replaced. For example, we can't apply this rewrite
    to `⟬X, each Y⟭` in this code, because the pack expansion in the deduced
    parameter list also contains the pack literal `⟬I, each type⟭`:
    ```carbon
    fn F[... ⟬X, each Y⟭:! ⟬I, each type⟭](... each __args: each ⟬X, each Y⟭);
    ```
    Notice that as a corollary of this rule, all the names in the name pack must
    have the same type.

See the [appendix](#pattern-match-typechecking-algorithm) for a more formal
discussion of the rewriting process.

### Typechecking pattern matches

To typecheck a pattern match between a tuple pattern and a tuple scrutinee, we
try to split and merge the segments of the scrutinee type so that it has the
same number of segments as the pattern type, and corresponding segments have the
same arity. For example, consider this call to `ZipAtLeastOne` (as defined in
the previous section):

```carbon
fn F[... each T:! type](... each t: Vector(each T), u: Vector(i32)) {
  ZipAtLeastOne(... each t, u);
}
```

The pattern type is `(... Vector(⟬First, each Next⟭))`, so we need to rewrite
the scrutinee type `(... Vector(each T), Vector(i32))` to have a single tuple
segment with an arity that matches `‖each Next‖+1`. We can do that by merging
the scrutinee segments to obtain `(... ⟬Vector(each T), Vector(i32)⟭)`. This has
a single segment with arity `‖each T‖+1`, which can match `‖each Next‖+1`
because the deduced arity `‖each Next‖` behaves as a deduced parameter of the
pattern, so they match by deducing `‖each Next‖ == ‖each T‖`.

When merging segments of the scrutinee, we don't attempt to form name packs and
replace them with invented names, but we also don't need to: we don't require a
merged scrutinee segments to have a single scalar component.

The search for this rewrite processes each pattern segment to the left of the
segment with deduced arity, in order from left to right. For each pattern
segment, it greedily merges unmatched scrutinee segments from left to right
until their cumulative shape is greater than or equal to the shape of the
pattern segment, and then splits off a scrutinee segment on the right if
necessary to make the shapes exactly match. Pattern segments to the right of the
segment with deduced arity are processed the same way, but with left and right
reversed, so that segments are always processed from the outside in.

See the [appendix](#appendix-type-system-formalism) for the rewrite rules that
govern merging and splitting.

Once we have the pattern and scrutinee segments in one-to-one correspondence, we
check each scalar component of the scrutinee type against the scalar component
of the corresponding pattern type segment (by construction, the pattern type
segment has only one scalar component). Since we are checking scalar components
against scalar components, this proceeds according to the usual rules of
non-variadic typechecking.

> **TODO:** Extend this approach to fall back to a complementary approach, where
> the pattern and scrutinee trade roles: we maximally merge the scrutinee tuple,
> while requiring each segment to have a single scalar component, and then
> merge/split the pattern tuple to match it, without requiring pattern tuple
> segments to have a single scalar component. This isn't quite symmetric with
> the current approach, because when processing the scrutinee we can't merge
> deduced parameters (scrutinees don't have any), but we can invent new `let`
> bindings.

## Appendix: Type system formalism

A _pack literal_ is a comma-separated sequence of segments, enclosed in `⟬⟭`
delimiters. A pack literal can appear in an expression, pattern, or name
context, and every segment must be valid in the context where the pack literal
appears (for example, the segments of a pack literal in a name context must all
be names). Pack literals cannot be nested, and cannot appear outside a pack
expansion.

### Explicit deduced arities

In this formalism, deduced arities are explicit rather than implicit, so Carbon
code must be desugared into this formalism as follows:

For each pack expansion pattern, we introduce a binding pattern `__N:! Arity` as
a deduced parameter of the enclosing full pattern, where `__N` is a name chosen
to avoid collisions. Then, for each binding pattern of the form `each X: T`
within that expansion, if `T` does not contain an each-name, the binding pattern
is rewritten as `each X: «T; __N»`. If this does not introduce any usages of
`__N`, we remove its declaration.

`Arity` is a compiler-internal type which represents non-negative integers. The
only operation it supports is `+`, with non-negative integer literals and other
`Arity`s. `Arity` is used only during type checking, so `+` has no run-time
semantics, and its only symbolic semantics are that it is commutative and
associative.

### Typing and shaping rules

The shape of an AST node within a pack expansion is determined as follows:

-   The shape of an arity coercion is the value of the expression after the `;`.
-   The shape of a pack literal is the concatenation of the arities of its
    segments.
-   The shape of an each-name expression is the shape of the binding pattern
    that declared the name.
-   If a binding pattern's name and type components have the same number of
    segments, and each name segment is an each-name if and only if the
    corresponding type segment's shape is not 1, then the shape of the binding
    pattern is the shape of the type expression. Otherwise, the binding pattern
    is ill-shaped.
-   For any other AST node:
    -   If all the node's children have shape 1, its shape is 1.
    -   If there is some shape `S` such that all of the node's children have
        shape either 1 or `S`, its shape is `S`.
    -   Otherwise, the node is ill-shaped.

> **TODO:** The "well-shaped" rules as stated are slightly too restrictive. For
> example, `⟬each X, Y⟭: «Z; N+1»` is well-shaped, and `(⟬each X, Y⟭, «Z; N+1»)`
> is well-shaped if the shape of `each X` is `N`.

The type of an expression or pattern can be computed as follows:

-   The type of `each x: auto` is `each __X`, a newly-invented deduced parameter
    of the enclosing full pattern, which behaves as if it was declared as
    `... each __X:! type`.
-   The type of an each-name expression is the type expression of the binding
    pattern that declared it.
-   The type of an arity coercion `«E; S»` is `«T; S»`, where `T` is the type of
    `E`.
-   The type of a pack literal is a pack literal consisting of the concatenated
    types of its segments. This concatenation flattens any nested pack literals
    (for example `⟬A, ⟬B, C⟭⟭` becomes `⟬A, B, C⟭`)
-   The type of a pack expansion expression or pattern is `...B`, where `B` is
    the type of its body.
-   The type of a tuple literal is a tuple literal consisting of the types of
    its segments.
-   If an expression or pattern `E` contains a pack literal or arity coercion
    that is not inside a pack expansion, the type of `E` is the type of the
    fully expanded form of `E`.

> **TODO:** address `...expand`, `...and` and `...or`.

### Reduction rules

Unless otherwise specified, all expressions in these rules must be free of side
effects. Note that every reduction rule is also an equivalence: the utterance
before the reduction is equivalent to the utterance after, so these rules can
sometimes be run in reverse (particularly during deduction).

Utterances that are reduced by these rules must be well-shaped (and the reduced
form will likewise be well-shaped), but need not be well-typed. This enables us
to apply these reductions while determining whether an utterance is well-typed,
as in the case of typing an expression or pattern that contains a pack literal
or arity coercion, above.

_Singular pack removal:_ if `E` is a pack segment, `⟬E⟭` reduces to `E`.

_Singular expansion removal:_ `...E` reduces to `E`, if the shape of `E` is
`(1,)`.

_Pack expansion splitting:_ If `E` is a segment and `S` is a sequence of
segments, `...⟬E, S⟭` reduces to `...E, ...⟬S⟭`.

_Pack expanding:_ If `F` is a function, `X` is an utterance that does not
contain pack literals, each-names, or arity coercions, and `⟬P1, P2⟭` and
`⟬Q1, Q2⟭` both have the shape `(S1, S2)`, then
`F(⟬P1, P2⟭, X, ⟬Q1, Q2⟭, «Y; S1+S2»)` reduces to
`⟬F(P1, X, Q1, «Y; S1»), F(P2, X, Q2, «Y; S2»)⟭`. This rule generalizes in
several dimensions:

-   `F` can have any number of arity coercion and other non-pack-literal
    arguments, and any positive number of pack literal arguments, and they can
    be in any order.
-   The pack literal arguments can have any number of segments (but the
    well-shapedness requirement means they must have the same number of
    segments).
-   `F()` can be any expression syntax other than `...`, not just a function
    call. For example, this rule implies that `⟬X1, X2⟭ * ⟬Y1, Y2⟭` reduces to
    `⟬X1 * Y1, X2 * Y2⟭`, where the `*` operator plays the role of `F`.
-   `F()` can also a be a pattern syntax. For example, this rule implies that
    `(⟬x1: X1, x2: X2⟭, ⟬y1: Y1, y2: Y2⟭)` reduces to
    `⟬(x1: X1, y1: Y1), (x2: X2, y2: Y2)⟭`, where the tuple pattern syntax
    `( , )` plays the role of `F`.
-   When binding pattern syntax takes the role of `F`, the name part of the
    binding pattern must be a name pack. For example, `⟬x1, x2⟭: ⟬X1, X2⟭`
    reduces to `⟬x1: X1, x2: X2⟭`, but `each x: ⟬X1, X2⟭` cannot be reduced by
    this rule.

_Coercion expanding:_ If `F` is a function, `S` is a shape, and `Y` is an
expression that does not contain pack literals or arity coercions,
`F(«X; S», Y, «Z; S»)` reduces to `«F(X, Y, Z); S»`. As with pack expanding,
this rule generalizes:

-   `F` can have any number of non-arity-coercion arguments, and any positive
    number of arity coercion arguments, and they can be in any order.
-   `F()` can be any expression syntax other than `...` or pack literal
    formation, not just a function call. Unlike pack expanding, coercion
    expanding does not apply if `F` is a pattern syntax.

_Coercion removal:_ `«E; 1»` reduces to `E`.

_Tuple indexing:_ Let `I` be an integer template constant, let `X` be a tuple
segment, and let `Ys` be a sequence of tuple segments.

-   If the arity `A` of `X` is less than `I+1`, then `(X, Ys).(I)` reduces to
    `(Ys).(I-A)`.
-   Otherwise:
    -   If `X` is not a pack expansion, then `(X, Ys).(I)` reduces to `X`.
    -   If `X` is of the form `...⟬«E; S»⟭`, then `(X, Ys).(I)` reduces to `E`.

### Equivalence, equality, and convertibility

_Pack renaming:_ Let `Ns` be a sequence of names, let `⟬Ns⟭: «T; N»` be a name
binding pattern (which may be a symbolic or template binding as well as a
runtime binding), and let `__A` be an identifier that does not collide with any
name that's visible where `⟬Ns⟭` is visible. We can rewrite all occurrences of
`⟬Ns⟭` to `each __A` in the scope of the binding pattern (including the pattern
itself) if all of the following conditions hold:

-   `Ns` contains at least one each-name.
-   No name in `Ns` is used in the scope outside of `Ns`.
-   No name occurs more than once in `Ns`.
-   No other pack literals occur in the same pack expansion as an occurrence of
    `⟬Ns⟭`.

_Expansion convertibility:_ `...T` is convertible to `...U` if the arity of `U`
equals the arity of `T`, and the scalar components of `T` are each convertible
to all scalar components of `U`.

_Shape equality:_ Let `(S1s)`, `(S2s)`, `(S3s)`, and `(S4s)` be shapes.
`(S1s, S2s)` equals `(S3s, S4s)` if `(S1s)` equals `(S3s)` and `(S2s)` equals
`(S4s)`.

### Pattern match typechecking algorithm

A full pattern is in _normal form_ if it contains no pack literals, and every
arity coercion is fully expanded. For example,
`[__N:! Arity](... each x: Vector(«i32; __N»))` is not in normal form, but
`[__N:! Arity](... each x: «Vector(i32); __N»)` is. Note that all user-written
full patterns are in normal form. Note also that by construction, this means
that the type of the body of every pack expansion has a single scalar component.
The _canonical form_ of a full pattern is the unique normal form (if any) that
is "maximally merged", meaning that every tuple pattern and tuple literal has
the smallest number of segments. For example, the canonical form of
`[__N:! Arity](... each x: «i32; __N», y: i32)` is
`[__N:! Arity](... each __args: «i32; __N+1»)`.

> **TODO:** Specify algorithm for converting a full pattern to canonical form,
> or establishing that there is no such form. See next section for a start.

If a function with type `F` is called with argument type `A`, we typecheck the
call by converting `F` to canonical form, and then checking whether `A` is
convertible to the parameter type by applying the deduction rules in the
previous sections. If that succeeds, we apply the resulting binding map to the
function return type to obtain the type of the call expression.

> **TODO:** Specify the algorithm more precisely. In particular, discuss how to
> rewrite `A` as needed to make the shapes line up, but don't rewrite `F` after
> canonicalization.

Typechecking for pattern match operations other than function calls is defined
in terms of typechecking a function call: We check a scrutinee type `S` against
a pattern `P` by checking `__F(S,)` against a hypothetical function signature
`fn __F(P,)->();`.

> **Future work:** Extend this approach to support merging the argument list as
> well as the parameter list.

#### Canonicalization algorithm

The canonical form can be found by starting with a normal form, and
incrementally merging an adjacent singular parameter type into the variadic
parameter type.

For example, consider the following function:

```carbon
fn F[First:! type, Second:! type, ... each Next:! type]
    (first: Vector(First), second: Vector(Second),
     ... each next: Vector(each Next)) -> (First, Second, ... each Next);
```

First, we desugar the implicit arity:

```carbon
fn F[__N:! Arity, First:! type, Second:! type, ... each Next:! «type; __N»]
    (first: Vector(First), second: Vector(Second),
     ... each next: Vector(each Next)) -> (First, Second, ... each Next);
```

Then we attempt to merge `Second` with `each Next` as follows (note that for
brevity, some of the steps presented here actually contain multiple independent
reductions):

```carbon
// Singular pack removal (in reverse)
fn F[__N:! Arity, First:! type, Second:! type, ... ⟬each Next:! «type; __N»⟭]
    (first: Vector(First), second: Vector(Second),
     ... each next: Vector(⟬each Next⟭)) -> (First, Second, ... ⟬each Next⟭);
// Pack expanding
fn F[__N:! Arity, First:! type, Second:! type, ... ⟬each Next:! «type; __N»⟭]
    (first: Vector(First), second: Vector(Second),
     ... each next: ⟬Vector(each Next)⟭) -> (First, Second, ... ⟬each Next⟭);
// Pack expanding
fn F[__N:! Arity, First:! type, Second:! type, ... ⟬each Next:! «type; __N»⟭]
    (first: Vector(First), second: Vector(Second),
     ... ⟬each next: Vector(each Next)⟭) -> (First, Second, ... ⟬each Next⟭);
// Pack expansion splitting (in reverse)
fn F[__N:! Arity, First:! type, ... ⟬Second:! type, each Next:! «type; __N»⟭]
    (first: Vector(First), ... ⟬second: Vector(Second),
                                each next: Vector(each Next)⟭)
    -> (First, ... ⟬Second, each Next⟭);
// Pack expanding (in reverse)
fn F[__N:! Arity, First:! type, ... ⟬Second, each Next⟭:! «type; __N+1»]
    (first: Vector(First), ... ⟬second, each next⟭: ⟬Vector(Second), Vector(each Next)⟭)
    -> (First, ... ⟬Second, each Next⟭);
// Pack expanding (in reverse)
fn F[__N:! Arity, First:! type, ... ⟬Second, each Next⟭:! «type; __N+1»]
    (first: Vector(First), ... ⟬second, each next⟭: Vector(⟬Second, each Next⟭))
    -> (First, ... ⟬Second, each Next⟭);
// Pack renaming
fn F[__N:! Arity, First:! type, ... each __A:! «type; __N+1»]
    (first: Vector(First), ... each __a: Vector(each __A))
    -> (First, ... each __A);
```

This brings us back to a normal form, while reducing the number of tuple
segments. We can now repeat that process to merge the remaining parameter type:

```carbon
fn F[__N:! Arity, First:! type, ... ⟬each __A:! «type; __N+1»⟭]
    (first: Vector(First), ... each __a: Vector(⟬each __A⟭))
    -> (First, ... ⟬each __A⟭);
// Pack expanding
fn F[__N:! Arity, First:! type, ... ⟬each __A:! «type; __N+1»⟭]
    (first: Vector(First), ... each __a: ⟬Vector(each __A)⟭)
    -> (First, ... ⟬each __A⟭);
// Pack expanding
fn F[__N:! Arity, First:! type, ... ⟬each __A:! «type; __N+1»⟭]
    (first: Vector(First), ... ⟬each __a: Vector(each __A)⟭)
    -> (First, ... ⟬each __A⟭);
// Pack expansion splitting (in reverse)
fn F[__N:! Arity, ... ⟬First:! type, each __A:! «type; __N+1»⟭]
    (... ⟬first: Vector(First), each __a: Vector(each __A)⟭)
    -> (... ⟬First, each __A⟭);
// Pack expanding (in reverse)
fn F[__N:! Arity, ... ⟬First, each __A⟭:! «type; __N+2»⟭]
    (... ⟬first, each __a⟭: ⟬Vector(First), Vector(each __A)⟭)
    -> (... ⟬First, each __A⟭);
// Pack expanding (in reverse)
fn F[__N:! Arity, ... ⟬First, each __A⟭:! «type; __N+2»⟭]
    (... ⟬first, each __a⟭: Vector(⟬First, each __A⟭))
    -> (... ⟬First, each __A⟭);
// Pack renaming
fn F[__N:! Arity, ... __B:! «type; __N+2»⟭]
    (... __b: Vector(__B))
    -> (... __B);
```

Here again, this is a normal form, and there is demonstrably no way to perform
any further merging, so this must be the canonical form.

> **TODO:** define the algorithm in more general terms, and discuss ways that
> merging can fail.

## Alternatives considered

-   [Member packs](/proposals/p2240.md#member-packs)
-   [Single semantic model for pack expansions](/proposals/p2240.md#single-semantic-model-for-pack-expansions)
-   [Generalize `expand`](/proposals/p2240.md#generalize-expand)
-   [Omit `expand`](/proposals/p2240.md#omit-expand)
-   [Support expanding arrays](/proposals/p2240.md#support-expanding-arrays)
-   [Omit each-names](/proposals/p2240.md#omit-each-names)
    -   [Disallow pack-type bindings](/proposals/p2240.md#disallow-pack-type-bindings)
-   [Fold expressions](/proposals/p2240.md#fold-expressions)
-   [Allow multiple pack expansions in a tuple pattern](/proposals/p2240.md#allow-multiple-pack-expansions-in-a-tuple-pattern)
-   [Allow nested pack expansions](/proposals/p2240.md#allow-nested-pack-expansions)
-   [Use postfix instead of prefix `...`](/proposals/p2240.md#use-postfix-instead-of-prefix-)
-   [Avoid context-sensitity in pack expansions](/proposals/p2240.md#avoid-context-sensitity-in-pack-expansions)
    -   [Fold-like syntax](/proposals/p2240.md#fold-like-syntax)
    -   [Variadic blocks](/proposals/p2240.md#variadic-blocks)
    -   [Keyword syntax](/proposals/p2240.md#keyword-syntax)
-   [Require parentheses around `each`](/proposals/p2240.md#require-parentheses-around-each)
-   [Fused expansion tokens](/proposals/p2240.md#fused-expansion-tokens)
-   [No parameter merging](/proposals/p2240.md#no-parameter-merging)
-   [Exhaustive function call typechecking](/proposals/p2240.md#exhaustive-function-call-typechecking)

## References

-   Proposal
    [#2240: Variadics](https://github.com/carbon-language/carbon-lang/pull/2240)
