Skip to content

Commit fe7db65

Browse files
Update InlineArray sugar proposal from x to of (#2861)
* Update 0483-inline-array-sugar.md Switch from `x` to `of` as the separator. Introduce an alternatives considered option that covers the naming of `InlineArray`. * Update 0483-inline-array-sugar.md * Update 0483-inline-array-sugar.md * Update 0483-inline-array-sugar.md * Update 0483-inline-array-sugar.md * Fix typos discovered by @ole and @benrimmington. * Minor editorial changes. * Update the review status in preparation for revision review. --------- Co-authored-by: Holly Borla <[email protected]>
1 parent eab7cae commit fe7db65

File tree

1 file changed

+82
-43
lines changed

1 file changed

+82
-43
lines changed

proposals/0483-inline-array-sugar.md

Lines changed: 82 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
# `InlineArray` Literal Syntax
1+
# `InlineArray` Type Sugar
22

33
* Proposal: [SE-0483](0483-inline-array-sugar.md)
44
* Authors: [Hamish Knight](https://github.com/hamishknight), [Ben Cohen](https://github.com/airspeedswift)
55
* Review Manager: [Holly Borla](https://github.com/hborla)
6-
* Status: **Active Review (May 2 - May 16, 2025)**
6+
* Status: **Active Review (June 6- June 16, 2025)**
77
* Implementation: On `main` under the `InlineArrayTypeSugar` experimental feature flag.
8-
* Review: ([pitch](https://forums.swift.org/t/pitch-inlinearray-type-sugar/79142)) ([review](https://forums.swift.org/t/se-0483-inlinearray-literal-syntax/79643))
8+
* Review: ([pitch](https://forums.swift.org/t/pitch-inlinearray-type-sugar/79142)) ([first review](https://forums.swift.org/t/se-0483-inlinearray-literal-syntax/79643))
99

1010
## Introduction
1111

@@ -21,131 +21,170 @@ let fiveIntegers: InlineArray<5, Int> = .init(repeating: 99)
2121

2222
Declaring this type is more cumbersome than its equivalent dynamically-sized array, which has sugar for the type syntax:
2323

24-
```
24+
```swift
2525
let fiveIntegers: [Int] = .init(repeating: 99, count: 5)
2626
```
2727

2828
This becomes more pronounced when dealing with multiple dimensions:
2929

30-
```
30+
```swift
3131
let fiveByFive: InlineArray<5, InlineArray<5, Int>> = .init(repeating: .init(repeating: 99))
3232
```
3333

34+
Almost every other language in a similar category to Swift – C, C++, Objective-C, Pascal, Go, Rust, Zig, Java, C# – has a simple syntax for their fixed-size array type. The introduction of a fixed-size array type into Swift should also introduce a shorthand syntax, in keeping with Swift's general approach of low ceremony and concise syntax. Swift further deviates from its peer languages by giving its _dynamic_ array type, `Array` (known in many other languages as `vector`) a sugared form. This can lead to an assumption that `Array` should be used under almost all circumstances, despite it having significant downsides in many uses (see further discussion in alternatives considered).
35+
3436
## Proposed solution
3537

3638
A new sugared version of the `InlineArray` type is proposed:
3739

3840
```swift
39-
let fiveIntegers: [5 x Int] = .init(repeating: 99)
41+
let fiveIntegers: [5 of Int] = .init(repeating: 99)
4042
```
4143

42-
The `x` here is the ASCII character, and is chosen to evoke the common shorthand use to represent "by", as in "4x4" or "2 in x 4 in".
43-
44-
Note that although it is used in the manner of an operator, `x` here serves more like a contextual keyword, similar to if the syntax were `[5 of Int]`.
44+
The choice of `of` forms something close to a grammatical phrase ("an array of five ints"). A short contextual keyword is also in keeping with Swift's tradition in other areas such as `in` or `let`.
4545

4646
## Detailed design
4747

48-
The new syntax consists of the value for the integer generic parameter and the type of the element generic parameter, separated by `x`.
48+
The new syntax consists of the value for the integer generic parameter and the type of the element generic parameter, separated by `of`.
4949

5050
This will be added to the grammar alongside the current type sugar:
5151

5252
> **Grammar of a type**
5353
> _type → sized-array-type_
5454
>
5555
> **Grammar of a sized array type**
56-
> _sized-array-type → [ expression `x` type ]_
56+
> _sized-array-type → [ expression `of` type ]_
5757
58-
Note that while the grammar allows for any expression, this is currently limited to only integer literals.
58+
Note that while the grammar allows for any expression, this is currently limited to only integer literals or integer type parameters, as required by the current implementation of `InlineArray`. If that restriction changes, so would the value allowed in the expression in the sugar.
5959

6060
The new sugar is equivalent to declaring a type of `InlineArray`, so all rules that can be applied to the generic placeholders for the unsugared version also apply to the sugared version:
6161

62-
```
62+
```swift
6363
// Nesting
6464
let fiveByFive: InlineArray<5, InlineArray<5, Int>> = .init(repeating: .init(repeating: 99))
65-
let fiveByFive: [5 x [5 x Int]] = .init(repeating: .init(repeating: 99))
65+
let fiveByFive: [5 of [5 of Int]] = .init(repeating: .init(repeating: 99))
6666

6767
// Inference from context:
68-
let fiveIntegers: [5 x _] = .init(repeating: 99)
69-
let fourBytes: [_ x Int8] = [1,2,3,4]
70-
let fourIntegers: [_ x _] = [1,2,3,4]
68+
let fiveIntegers: [5 of _] = .init(repeating: 99)
69+
let fourBytes: [_ of Int8] = [1,2,3,4]
70+
let fourIntegers: [_ of _] = [1,2,3,4]
7171

7272
// use on rhs
73-
let fiveDoubles = [5 x _](repeating: 1.23)
73+
let fiveDoubles = [5 of _](repeating: 1.23)
7474
```
7575

7676
The sugar can also be used in place of the unsugared type wherever it might appear:
7777

78-
```
79-
[5 x Int](repeating: 99)
80-
MemoryLayout<[5 x Int]>.size
81-
unsafeBitCast((1,2,3), to: [3 x Int].self)
78+
```swift
79+
[5 of Int](repeating: 99)
80+
MemoryLayout<[5 of Int]>.size
81+
unsafeBitCast((1,2,3), to: [3 of Int].self)
8282
```
8383

84-
There must be whitespace on either side of the separator i.e. you cannot write `[5x Int]`. There are no requirements to balance whitespace, `[5 x Int]` is permitted. A new line can appear after the `x` but not before it, as while this is not ambiguous, this aids with the parser recovery logic, leading to better syntax error diagnostics.
84+
There must be whitespace on either side of the separator; i.e., you cannot write `[5of Int]`. There are no requirements to balance whitespace; `[5 of Int]` is permitted. A new line can appear after the `of` but not before it, as while this is not ambiguous, this aids with the parser recovery logic, leading to better syntax error diagnostics.
8585

8686
## Source Compatibility
8787

8888
Since it is not currently possible to write any form of the proposed syntax in Swift today, this proposal does not alter the meaning of any existing code.
8989

9090
## Impact on ABI
9191

92-
This is purely compile-time sugar for the existing type. It is resolved at compile time, and does not appear in the ABI nor rely on any version of the runtime.
92+
This is purely compile-time sugar for the existing type. It is resolved at compile time and does not appear in the ABI nor rely on any version of the runtime.
9393

9494
## Future Directions
9595

9696
### Repeated value equivalent
9797

98-
Analogous to arrays, there is an equivalent _value_ sugar for literals of a specific size:
98+
Analogous to arrays, there is an equivalent *value* sugar for literals of a specific size:
9999

100-
```
101-
// type inferred to be [5 x Int]
102-
let fiveInts = [5 x 99]
103-
// type inferred to be [5 x [5 x Int]]
104-
let fiveByFive = [5 x [5 x 99]]
100+
```swift
101+
// type inferred to be [5 of Int]
102+
let fiveInts = [5 of 99]
103+
104+
// type inferred to be [5 of [5 of Int]]
105+
let fiveByFive = [5 of [5 of 99]]
105106
```
106107

107108
Unlike the sugar for the type, this would also have applicability for existing types:
108109

109-
```
110+
```swift
110111
// equivalent to .init(repeating: 99, count: 5)
111-
let dynamic: [Int] = [5 x 99]
112+
let dynamic: [Int] = [5 of 99]
112113
```
113114

114115
This is a much bigger design space, potentially requiring a new expressible-by-literal protocol and a way to map the literal to an initializer. As such, it is left for a future proposal.
115116

117+
However, the choice of syntax for the type sugar has a significant impact on the viability of this future direction (see alternatives considered). Given the potential benefit of such a value syntax, any choice for the type sugar should consider its future extension to value sugar.
118+
119+
[^expressions]: It could also cause confusion once expressions are allowed for declaring an `InlineArray` i.e. `[5 * 5 * Int]` would be allowed.
120+
116121
### Flattened multi-dimensional arrays
117122

118-
For multi-dimensional arrays, `[5 x [5 x Int]]` could be flattened to `[5 x 5 x Int]` without any additional parsing issues. This could be an alternative considered, but is in future directions as it could also be introduced as sugar for the former case at a later date.
123+
For multi-dimensional arrays, `[5 of [5 of Int]]` could be flattened to `[5 of 5 of Int]` without any additional parsing issues. This could be an alternative considered but is in future directions as it could also be introduced as sugar for the former case at a later date.
119124

120125
## Alternatives Considered
121126

127+
### Indication of the "inline" nature via the sugar.
128+
129+
The naming of `InlineArray` incorporates important information about the nature of the type – that it includes its values inline rather than indirectly via a pointer. This name was chosen over other alternatives such as `FixedSizeArray` because the "inline-ness" was considered the more fundamental property, and so a better driver for the name.
130+
131+
This has led to suggestions that this inline nature is important to include in the sugar was well. However, the current state privileges the position of `Array` as the only array type that is sugared. This implies that `Array` is the right choice in all circumstances, with inline arrays being a rare micro-optimization. This is not the case.
132+
133+
For example, consider a translation of this code from the popular [Ray Tracing in One Weekend](https://raytracing.github.io/books/RayTracingInOneWeekend.html#thevec3class) tutorial:
134+
135+
```cpp
136+
class vec3 {
137+
public:
138+
double e[3];
139+
140+
double x() const { return e[0]; }
141+
double y() const { return e[1]; }
142+
double z() const { return e[2]; }
143+
// etc
144+
}
145+
```
146+
147+
The way in which Swift privileges `[Double]` with sugar strongly implies you should use that in this translation. This would be the wrong choice. Every access to those coordinate accessors would need to check the bounds (because while the author might ensure that the value of `e` will only ever have length 3, the compiler cannot easily know this) and, in the case of mutation, a check for uniqueness of the pointer. It would also make `vec3` a nontrivial type, which has significant performance implications wherever it is used. `InlineArray<3, Double>` has none of these problems.
148+
149+
Other examples include using nested `Array` types i.e. using `[[Double]]` to represent a matrix – which would also have significant negative consequences depending on the use case, compared to using an `InlineArray` to model the same values. In other cases, the "copy on write" nature of `Array` can mislead users into thinking that copies are risk-free, when actually copying an array can lead to "defeating" copy on write in subtle ways that can cause difficult-to-hunt-down performance issues. In all these cases, you need to pick the right one of two options for the performance goals you are trying to achieve.
150+
151+
It is likely that Swift's choice (deviating from many of its peers) to emphasize its dynamic array through sugar, has led to a negative impact on the culture of writing performant Swift code, with the nicely sugared dynamic arrays (and array value literals) being over-favored. This is not intended to make the case that Swift should _not_ have this sugar. Dynamic arrays are widely useful and Swift's readability goals are improved by Swift having a concise syntax for creating them. But writing performant Swift code inevitably involves having an understanding of the underlying performance characteristics of _all_ the types you are using, and syntax or type naming alone cannot solve this.
152+
153+
Of course, all this only matters when you are trying to write code that maximizes performance. But that is a really important use case for Swift. The goal for Swift is a language that is as safe and enjoyable to write as many high-level non-performant languages, but also can achieve peak performance when that is your goal. And the idea is that when you are targeting that level of performance, you don't have to go into "ugly, no longer nice swift" mode to do it, with nice sugared `[Double]` replaced with less pleasant full type name of `InlineArray` – something a user coming from Go or C++ or Rust might find a downgrade. Similarly, attempting to incorporate the word "inline" into the sugar e.g. `[5 inline Int]` creates a worst of both worlds solution that many would find offputting to use, without solving the fundamental issue.
154+
155+
For these reasons, we should be considering `InlineArray` a peer of `Array` (even if the need for it is less common – just not "niche"), and providing a pleasant to use sugar for both types.
156+
122157
### Choice of delimiter
123158

124159
The most obvious alternative here is the choice of separator. Other options include:
125160

161+
- `[5 by Int]` is similar to `of`, but is less applicable to the value syntax (`[5 by 5]` doesn't read as an array of 5 instances of 5), without being clearer for the type syntax.
162+
- `[5 x Int]`, using the ascii letter `x`, as an approximation for multiplication, reflecting common uses such as "a 4x4 vehicle". This was the choice of a previous revision of this proposal.
126163
- `[5 * Int]`, using the standard ASCII symbol for multiplication.
127164
- `[5 ⨉ Int]`, the Unicode n-ary times operator. This looks nice but is impractical as not keyboard-accessible.
128-
- `[5; Int]` is what Rust uses, but appears to have little association with "times" or "many". Similarly other arbitrary punctuation e.g. `,` or `/` or `#`.
129-
- `[5 of Int]` is more verbose than `x` but could be considered more clear. It has the upside or downside, depending on your preference, of being almost, but not quite, grammatical.
130-
- `:` is of course ruled out as it is used for dictionary literals.
165+
- `[5; Int]` is what Rust uses, but appears to have little association with "times" or "many". Similarly other arbitrary punctuation e.g. `,` or `/`. `:` is of course ruled out as it is used for dictionary literals.
166+
- `#` does have an association with counts in some areas such as set theory, but is used as a prefix operator rather than infix i.e. `[#5 Int]`. This is less expected than the infix form, and could also be read as "the fifth `Int`". It is also unclear how this would work with expressions like an array of size `5*5`.
167+
- No delimiter at all i.e. `[5 Int]`. While this might be made to parse, the lack of any separator is found unsettling by some users and is less visually clear, especially once expressions are allowed instead of the `5`.
131168

132-
Note that `*` is an existing operator, and may lead to ambiguity in future when expressions can be used to determine the size: `[5 * N * Int]`. `x` is clearer in this case: `[5 * N x Int]`. It also avoids parsing ambiguity, as the grammar does not allow two identifiers in succession. But it would be less clear if `x` also appeared as an identifier: `[5 * x x Int]` (which is not yet permitted but may be in future use cases).
169+
Note that `*` is an existing operator, and may lead to ambiguity in future when expressions can be used to determine the size: `[5 * N * Int]`. `of` is clearer in this case: `[5 * N of Int]`. It also avoids parsing ambiguity, as the grammar does not allow two identifiers in succession. This becomes more important if the future direction of a value equivalent is pursued. `[2 * 2 * 2]` could be interpreted as `[2, 2, 2, 2]`, `[4, 4,]`, or `[8]`.
133170

134-
This becomes more important if the future direction of a value equivalent is pursued. `[2 * 2 * 2]` could be interpreted as `[2, 2, 2, 2]`, `[4, 4,]`, or `[8]`.
171+
Since `of` cannot follow another identifier today, `[of of Int]` is unambiguous,[^type] but would clearly be hard to read. This is likely a hypothetical concern rather than a practical one since `of` is very rare as a variable name. The previous proposal's use of `x` involved a variable name that was more common.
135172

136-
Since `x` cannot follow another identifier today, `[x x Int]` is unambiguous,[^type] but would clearly be hard to read. This is likely a hypothetical concern rather than a practical one. While `x` is used often in scratch code for a local variable, a more meaningful name is usually preferable, and this would be especially the case if it is found being used for the size of an array literal. In addition, while `i`, `j`, or `n` are often legitimate counters that might be suited to the size of an array, `x` is generally not used for such things.
173+
`x` is also less clear when used for the value version: `[5 x 5]` can be parsed unambiguously, but looks similar to five times five, and so visually has the same challenges as with the `*` operator, even if this isn't a problem for the compiler.
137174

138-
[^type]: or even `[x x x]`, since `x` can be a type name, albeit one that defies Swift's naming conventions.
175+
[^type]: or even `[of of of]`, since `of` can be a type name, albeit one that defies Swift's naming conventions.
139176

140177
Another thing to consider is how that separator looks in the fully inferred version, which tend to start to look a little like ascii diagrams:
141178

142179
```
180+
[_ of _]
143181
[_ x _]
144182
[_ * _]
145183
[_; _]
146-
[_ of _]
147184
```
148185

186+
Of all these, the `of` choice is less susceptible to the ascii art problem.
187+
149188
### Order of size and type
150189

151190
The order of size first, then type is determined by the ordering of the unsugared type, and deviating from this for the sugared version is not an option.
@@ -156,6 +195,6 @@ In theory, when using integer literals or `_` the whitespace could be omitted (`
156195

157196
### Choice of brackets
158197

159-
`InlineArray` has a lot in common with tuples – especially in sharing "copy on copy" behavior, unlike regular `Array`. So `(5 x Int)` may be an appropriate alternative to the square brackets, echoing this similarity.
198+
`InlineArray` has a lot in common with tuples – especially in sharing "copy on copy" behavior, unlike regular `Array`. So `(5 of Int)` may be an appropriate alternative to the square brackets, echoing this similarity. However, tuples and `InlineArray`s remain very different types, and it could be misleading to imply that `InlineArray` is "just" a tuple.
160199

161-
Beyond varying the separator, there may be other dramatically different syntax that moves further from the "like Array sugar, but with a size argument".
200+
Beyond varying the separator, there may be other dramatically different syntax that moves further from the "like Array sugar, but with a size argument". For example, dropping the brackets altogether (i.e. `let a: 5 of Int`). However, these are probably too much of a departure from the current Swift idioms, so likely to cause further confusion without any real upside.

0 commit comments

Comments
 (0)