Being reminded of matrix_float4x4
has softened me quite a bit, but I still don't like any of the n x T
variations, preferring of
instead.
Then, the compiler must allow [1024 ĺ㎠UInt8]
or [UInt8 ă 1024ăś]
clearly when your locale is ja_JP
.
Joking aside, we'd better consider people who write programs in Swift have various cultural/linguistic backgrounds, even though Swift syntax is definitely based on English.
In my country, for example, "1024 instances of UInt8
" is more often represented by UInt8 Ă 1024
rather than 1024 Ă UInt8
.
Thus, [5 x 99]
(only described in "Future Directions" though) will be interpreted as "99 integers whose values are all 5" in my country.
I think any operators (e.g. x
, Ă
, or *
) themselves have essentially universal meanings, but word orders don't; no matter which operator is chosen.
In that sense, I want to support of
keyword (if sugar is necessary) because of
is evidently an English word.
I mean, when we see 1024 of UInt8
, we will parse it in English word order because we can easily recognize it's based on English.
That's somewhat ironic, but we should stick to English words here if we value diversity.
Youâre right. Iâve actually made the mistake several times now reversing the proposed meaning of x
as described in the current proposal.
I never thought that I would be so easily overcome by that mistake. By user example, mine, I can attest to itâs confusing nature, lending more evidence to myself for why of
is superior.
I believe it would be a mistake for us to accept "x" as proposed without considering the implications on the future directions to find ourselves in a situation we don't quite like the outcome:
[1 + 2 x 3 + 4] // đ¤"that's [11], right?"
I don't think this would be advantageous to have in the language. Tuples are not well-suited for storing large amounts of data, so a syntax making that easier feels undesirable.
In my experience, I rarely need to write comma-separated lists with repeated values and non-repeated values interspersed with each other, so I donât think I'd find this syntax much more useful than a repetition-only syntax.
If this is something we want to sugar, I feel it should be done with the repeat
keyword, since thatâs the keyword variadic generics uses for splatting.
I don't see why we should reuse the InlineArray
syntax for non-empty Array
s. [some of some T]
would still be possible if inline arrays used x
.
I just want to express my support for introducing the syntactic âsugarâ for InlineArray
now, rather than delaying the decision. A strong point has already been raised in this thread: there havenât been any new syntax ideas in the past decade. I agree â it seems unlikely that something better will come along, and waiting might only postpone a solid starting point for adopting InlineArray
.
While Iâm not entirely sold on x
, it does suggest a kind of cartesian product to me when combined with a type, and it reads well in type definitions, especially for multidimensional cases. Iâm less convinced about how it might extend to future literal syntax â but that part still seems open. Iâm also not particularly fond of of
.
Could someone clarify what exactly is meant by âkeyboard-accessibleâ? For example, would the following be ruled out?
var adjList: [5 | [9 | Int]] = [5 | [9 | 0 ]]
var adjList: [5 ° [9 ° Int]] = [5 ° [9 ° 0 ]]
Thank you for that. Could you tell me how you plan on using InlineArray in general. I think it would be helpful to understand the communityâs use cases.
Currently a lot of C is imported as Tuples, and many of those declarations are repeated Types in various projects. So for the declaration site, they absolutely make sense.
Some other use cases.
- Tile Maps
let tileMap = [5 of 0, 3 of 1, 4 of 2] grass, then water, then stone
- Arduino singlas
let signal = [5 of HIGH, 5 of LOW, 2, 2, 3 of 10, 1]
- Programming a string of lights.
let lights = [10 of [255, 0, 0], 10 of [255, 255, 255]], 10 of [0, 0, 255]
And if it were required because the API was imported as tuples..
let lights = (10 of (255, 0, 0), 10 of (255, 255, 255)], 10 of (0, 0, 255))
I am certain the community can do better than me at coming up with examples. I don't do embedded style programing often. I do recall that there are some setup signals that often require repeating, I just can't think of it off the top of my head.
Tuples as they exist today are not suited to this use case for two reasons: 1) Each element is independently type checked, meaning that type checking gets slower with each added element. 2) The last element of a tuple isn't padded to it's alignment, which means it's not safe to pass homogeneous tuples to functions expecting to copy around C arrays.
The first would not apply to an (n x T)
tuple as it would obviously not need to infer the type of every element, and the later would not apply here as (n x T)
is being suggested as sugar for InlineArray
instead of tuple.
In the post I was replying to, (5 x Int)
was meant to be syntax sugar for a tuple, with [5 x Int]
being sugar for InlineArray
.
Another big reason tuples are ill-suited for storing a lot of instances is that Swift's calling convention always destructures tuples. This means that if you pass a tuple of 1024 UInt8
s to a function, that's equivalent to passing 1024 UInt8
parameters to it. This causes massive amounts of copying and a huge increase in code size. An inline array of 1024 UInt8
s, on the other hand, is passed indirectly (by pointer), eliminating the need to make a copy of each element just to pass the array to the function.
Is that the case? I was under the impression that a caveat of using InlineArray
is that the contents are copied when passed as a parameter so one has to be extra careful.
Example
The code
@inline(never)
func sum<let N: Int>(_ array: borrowing InlineArray<N, UInt8>) -> UInt8 {
var result: UInt8 = 0
for i in 0..<array.count {
result &+= array[i]
}
return result
}
let randVal = UInt8.random(in: 0...99)
var arr = InlineArray<1024, UInt8>(repeating: randVal)
let _ = sum(arr)
generates (Compiler Explorer)
generic specialization <1024> of output.sum<let A>(Swift.InlineArray<A, Swift.UInt8>) -> Swift.UInt8:
sub rsp, 1032
mov rsi, rdi
lea rdi, [rsp + 8]
mov edx, 1024
call memcpy@PLT
pxor xmm0, xmm0
mov eax, 112
...
add rsp, 1032
ret
Notice the sub
and add
of rsp
and the call to memcpy
.
I have not seen it mentioned in this thread, but x
is also used in hex literals, which negatively impacts readability:
[0x10] // [Int]
[1 x 10] // InlineArray<1, Int>
I think the value sugar case tips the scales in favor of of
over x
for me.
Separator option not mentioned in the proposal:
In music notation, |: :|
is used to denote repetition (written in ascii), so perhaps
[5 |: Int]
could be an option if we're inventing symbols for n
repetitions.
In Swift, arguments larger than a certain size (4 words on most platforms, 3 words on i386) are always passed via pointer. I don't believe there's a need to copy the value before passing it via pointer, since Swift requires exclusive access to memory for mutation.
I don't believe this copy is required by Swift's calling convention. I think it may be a bug â this copy doesn't seem to be necessary, and avoiding unnecessary copies seems to be a goal for this type. Someone who works on the Swift compiler would probably know about Swift's calling convention and InlineArray
's intended semantics better than I do.
Note that if you remove <let N: Int>
and directly specify that array
is an InlineArray<1024, UInt8>
, then no copy occurs at all. (Compiler Explorer)
Iâd much prefer a comma as delimiter.
var items: [5, Int] = âŚ
The inferred case [_, _]
looks familiar in swift
Why use borrowing here? I was under the impression that it's totally fine (from performance POV) to pass a large struct as a parameter as it would be passed via the pointer and no actual copy is done, is borrowing buying you anything?
Nice one. I'd like to add that while your example shows the confusion of using x
in value literals, it's the same for such usage in type literals:
[0xA] // a value of [10]
[1 x A] // a type of InlineArray<1, A>
Also, start from your idea of musical notation, I'm wondering is it possible to reuse the existing keyword repeat
from value/type packs
[5 repeat Int]
// or
[repeat 5 Int]
// or
(repeat 5 Int)
It doesn't make a difference here since arguments are borrowing
by default for copyable types. I hope I'm wrong about the copying, and I hope that I can trust that InlineArray
s are passed by address. The code generation though suggests otherwise.
If I remember it correctly, the default ownership syntax for a parameter of a normal function is implicitly borrowing
already. Also, manually stating borrowing
does not control how the value is passed at low level, the compiler will decide whether to pass an address or perform a "bit-wise borrowing".
Reference: Doesn't Swift borrow immutable structs by default? - #7 by Joe_Groff
+1 on the proposal from me.
I didnât plan to comment on this review. But since the discussion has been quite long, somewhat heated, and in my opinion surprisingly negative against the proposal, I just wanted to show, that some of us actually like the proposed changes.
I have read the proposal in detail as well as most of the pitches and threads leading up to the proposal.
When it comes to syntactic sugaring, personal preference seems to be influenced more by differences in taste and experiences with prior art, than by logic and verifiable facts.
From my point of view, the proposed usage of x
as a contextual keyword when declaring an InlineArray<5, Int>
feels natural in general and fits well into Swift. But I also (mostly) understand why some donât agree. Using e.g. of
as an alternative is okay, I guess, but doesnât scale as well to the future directions with multiple dimensions like:
var bitmap: [1024 x 768 x RGB]
Looks much more natural to me than:
var bitmap: [1024 of 768 of RGB]
I personally donât find the usage of x
to be âtoo cuteâ or âunseriousâ. On the contrary, itâs a well established usage in many situations for specifying a number of items (x
= times
) or e.g. the dimension of an area. Especially when usage of a proper times
/multiplication
symbol is impractical (i.e. Ă
or U+00D7).
Another debated aspect is the (possible) future literal sugar with the type inferred by the initialized default value:
var row = [32 x 17]
Is it 32 integer values initialized to 17, or the other way around? In seems too ambiguous for my taste. And I also suspect, in many practical situations, that the inferred type of Int
is not âpreciseâ enough and would preferably be expressed as e.g. UInt8
or Int32
.
One suggestion to improve the clarity of the literal syntax is to require the âvalueâ to be expressed using an initializer expression, possible spiced with a range operator:
var row = [32 x Int32(17)]
var buffer = [128 x UInt8(0)]
var digits = [10 x UInt8(48âŚ)]
var blue = [1024 x 768 x RGB(0, 0, 255)]
var sound = [512 x Double(0.0)]
Not as short or pretty as the proposed literal, but clearer and easier to understand, if you ask me.
Interesting, how do you feel about x
being used in various proposed contexts that would make wider use of the syntax?
See this post for examples.
In those contexts, using of
is probably cleaner than x
, but only slightly. It would still work okay with x
.
But Iâm not completely convinced, that extending the concept to that âextremeâ is necessary or wise. It easily gets hard to read and understand, regardless of which keyword gets chosen.
At the same time, itâs a nice idea and feels neat for the reasonably complex cases. So Iâm a bit torn.
Still I wouldnât mind using x
if this would be implemented in the end. When things get complicated use parentheses to clarify or change the name of the variable if it clashes with x
.
Not ideal maybe, but in my view itâs more important to get the main usage as sugar for InlineArray
and the literal sugar right, is more important than a possible generic extension to series of values.