Skip to content

Update 0483-inline-array-sugar.md #2896

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions proposals/0483-inline-array-sugar.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,17 @@ class vec3 {
}
```

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.
The way in which Swift privileges `[Double]` with sugar strongly implies you should use that in this translation. Doing so would have significant performance downsides:
- Creating new instances of `Vec3` requires a heap allocation, and destroying them require a free operation.
- `Vec3` could no longer be `BitwiseCopyable`, instead requiring a reference counting operation to make a copy.
- Access to a coordinate would require pointer chasing, and a contiguous array of `Vec3` objects would not be guaranteed to exist in contiguous memory.
- 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. `InlineArray<3, Double>` has none of these problems.

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.
Other examples include the use of nested `Array` types i.e. using `[[Double]]` to represent a matrix of known size, which would also have noticeable negative performance impact depending on the use case, compared to using an `InlineArray<InlineArray<Double>>` (or perhaps `[InlineArray<Double>]`) to model the same values. Today, this is possible via custom subscripts that logically represent the inner array within a single `[Double]`, but the introduction of `InlineArray` introduces other potentially more ergonomic options.

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.
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.

Swift's choice (deviating from many of its peers) to only have a dynamic array type, and to emphasize the utility of this type through sugar, has led to a shaping of the culture of writing Swift code that favors the sugared dynamic array even when this leads to otherwise-avoidable negative performance impact. 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.

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.

Expand Down