I concur. In the other thread I accidentally posted (what I attached below). I wanted to add as well that I fully believe that most of us in this forum would function just fine with x
, we're technical people who like technical things; I am largely in objection because (1) it doesn't fit Swift, (2) it doesn't convey as well what a keyword like of
would, and (3) in certain contexts it really does just look like multiplication (i.e [5 x 3]
) -- where as (of
) doesn't suffer this. So while I am sympathetic to the prior art reasoning, I think that this is a case where Swift should go it's own way -- especially since we want the keyword to be used in more context that what is currently available in LLVM IR. (I've enumerated these before so I won't do it again, you can read here SE-0483: `InlineArray` Literal Syntax - #299 by austintatious)
I still believe of
would be a lot better than x
because its not a common variable name and fits the kind of short "flow" word that is used as keywords (in
, as
, for
, let
, etc.)
// from
let t1 = InlineArray<100, Int> = .init(repeating: 0)
let t2 = InlineArray<3, Int> = [0, 1, 2]
let t3: InlineArray = [0, 1, 2]
// to
let t1 = [100 of 0]
let t2: [3 of Int] = [0, 1, 2]
let t2: InlineArray = [0, 1, 2]
Which can easily fit in other contexts:
// nesting?
let t4 = [8 of [3 of 0.0]]
t4[3][2] = 4
// merged with constant expressions
let t5 = [8 * 3 of 0.0]
t5[3*8+2] = 4
// aliasing
typealias Coord<let n: Int> = [n of Float]
let t6: [8 of Coord<3>] = [8 of [3 of 0.0]]
With my personal use for this feature typically looking like this:
var ram = [65536 of UInt16](repeating: 0)
var rom = [32768 of UInt16](repeating: 0)
var mmu: [6 of MemoryPage] = [.rom(0), .rom(1), .ram(0), .ram(1), .device]
I'd also use it extensively for vectors but then I'd most likely wrap it. I'd however want to keep the short literal syntax.
public struct Vector3f {
public var components: [3 of Float]
// ...
var x: Float { get set }
var y: Float { get set }
var z: Float { get set }
var xy: Vector2f { get set }
// ...
}
This is interesting as well, because visually if it were x
(which I am glad we are making great arguments against), it would be let t5 = [8 * 3 x 0.0]
which looks confusing due to our background knowledge in even high school algebra. (most of us have not gone into set theory).
Another great reason that x
is the wrong choice.
If my use of InlineArray
is not public
, then can't I get a fairly terse syntax with just typealias IA = InlineArray
? The syntax IA<5, Int>
isn't much longer than [5 x Int]
. I wouldn't want IA
to be public, but maybe public declarations shouldn't use as much sugar anyway.
That doesn't solve the nested type problem, though: IA<5, IA<5, Int>>
is much worse than (future direction) [5 x 5 x Int]
. I think this nesting is a real problem that the future direction solves.
That leads me to a question.
In SE-0452: Integer Generic Parameters, one future direction is integer parameter packs.
If integer parameter packs were already part of Swift today, would InlineArray
be declared InlineArray<let each count: Int, Element>
, such that I could write InlineArray<5, 5, Int>
or even InlineArray<5, 6, 7, 8, 9, Int>
? That would solve the nesting problem. And if the nesting problem were solved that way, would you still want this proposal's sugar? I don't think I would.
Another advantage (...) has over [...] is that the compiler could assume you mean an InlineArray
rater than a Tuple
if the contents were homogeneous, unless you specify. For [...], an Array
already is homogeneous so that isn't a distinction.
But yes, nothing quite hits yet.
(FWIW, I really liked Ben's write up of the different weaknesses. I hope that gets moved into a blog post. For myself I don't actually think InlineArray
is any more "dangerous" than Array
. I think failing to understand that they are non-trivially different, that indeed there is a difference to be curious about, is what presents the danger.)
I think the close it can get without a syntax too different from what exists is
[Int]<2>
.
This direction is also reasonable in my opinion.
It would have the following advantages.
-
Simply adding a size-specified syntax to the existing array syntax.
-
The size specification syntax uses Integer Generic, which is easy to understand.
-
It can be easily extended to multi-dimensional size specification in the future (
<1, 2, 3>
).- Assuming that multi-dimensional array accesses are specified with multiple index arguments (
s = x[0, 1, 2]
), multi-dimensional size specifications should be comma-separated.
- Assuming that multi-dimensional array accesses are specified with multiple index arguments (
I wanted to make sure that I had the correct understanding of how important the Inline
part of InlineArray
was, so I went back and skimmed the 2nd review thread and acceptance post. I found a lot of disagreement on how important Inline
is, however this is in the acceptance post, which seems most relevant:
That feels opposed to the proposed sugar which hides the inline property and only shows the fixed-size property. I expect [5 x Int]
to be easy & appealing for users who won't realize that they're even using an InlineArray
.
Something else I realized while reviewing those threads is that there have been several other types of Array
's discussed for the future, and this sugar could impact them. If we're going to have sugar for InlineArray
I think we need to consider the impact this sugar will have on future array types like a ContiguousArray
or RigidArray
, especially since some of the proposed sugar could easily extend to support them ([5 inline Int]
, [5 rigid Int]
, ...), while others don't appear to ([5 x Int]
, [5 <what goes here?> Int]
, ...).
There's a simple reason for this: I barely knew about LLVM's IR representation. I'd seen it in the past, but it wasn't on my mind when writing the proposal. I can certainly add it to the text now it's been pointed out, of course.
I don't think it's quite prior art as we tend to use the term to justify syntax. I think that is more commonly thought of as prior art programmers will have seen elsewhere in Swift in a similar situation, or in other languages.
That said, I do think that the LLVM usage demonstrates clearly that x
in this context is a reasonable choice that has been made before and not (as has been claimed elsewhere) some out there idea that has no merit. The prior thread that's been brought up from the early mail group days, where x
was batted around as a good option without any real pushback, is further evidence of this. And I was also reminded recently that on Apple platforms, the simd
import gives you types such as matrix_float4x4
, another similar usage (though I do realize this is float4x4
not 4x4xfloat
.
This doesn't mean it is better than of
, of course. Personally, though, I remain in favor of x
as the solution (with of
being a good runner up).
Another similar (and more explicitly parallel) precedent for x
is the ARM Neon types: int8x16_t
is a vector of 16 int8_t
s. A uint32x4x2_t
is a structure of two vectors of four uint32_t
s.
Now, of course, what's different about these is that it's just part of the name, not an operator (or keyword). But I think it's still pretty clear precedent for the view that "x
is a perfectly natural way to spell this in Computer English."
Do LLVM and NEON support CoW? That was one of the distinguishing features of Swiftâs InlineArray, and itâs not clear to me how the equivalents are handled elsewhere, and how we should interpret them here.
I agree that it has merit within the very specific use site of [n x T]
. If we plan to confine its usage there then I can agree fully with no concerns.
However, IMO it does start to look odd in all the other context that have been explored in this thread.
InlineArray is not CoW based.
This is probably the most convincing argument for x
for me so far.
Has [Int x 4]
been discussed? I don't recall, and can't find anything about it in the proposal. I've been against x
so far, but after seeing the float4x4
example, for some reason [Int x 4]
sits much better with me.
It was discussed in the pre-pitch, and the core team has expressed a desir that the number of elements be placed first to match its position in InlineArray<n, T>
That's because [Int x 4]
screams [Int]
, while [4 x Int]
... [4]
?
I already explained that in SE-0483: `InlineArray` Literal Syntax - #280 by dima_kozhinov .
Interesting, how do you feel about the usage of x
in the other positions that have been proposed?
The original post has heaps of comparisons for other proposed operators, keywords and delimiters among some arguments that I think you'd find compelling. I would love to know what you think.
I would love to draw your attention to [1 , 4 * 2 x 4, 3]
vs [1 , 4 * 2 of 4, 3]
I think your highlighted example is in the wrong order?
// [4,4,4,4,4,4,4,4]
let t1 = [4 * 2 x 4]
let t1 = [4 * 2 of 4]
// [8,8,8,8]
let t2 = [4 x 4 * 2]
let t2 = [4 of 4 * 2]
// [20,20,20,20,20]? not sure about the operator precedence
let t3 = [2 * 3 x 4 * 5]
let t3 = [2 * 3 of 4 * 5]
Youâre right!! Iâll go back and fix those. I thought I was keeping it straight after I confused the Rust order (which is reversed, I think) now I need to rethink they too. Thank you
Exactly.
What I enjoy about your example (and thank you again for catching my mistakes), is that it highlights at least for me that x
appears so much more like a standard math operator, but of
feels like it more clearly would be interpreted the same as [(2 * 3) of (4 * 5)]