I understand as I used to make the same argument; however, this would kind of preclude us from being able to use () to construct repeating fields in a tuple. If we decided that we wanted to use that syntax..
let t: (3 of Int) // (Int, Int, Int)
Under your proposed meaning, that would just equal the same thing as bare 3 of Int. As tuples of one element don’t exist in Swift.
I would hate to see of confined.
If it is absolutely true that no other use of of will be used then ok. Bare of is fine.
+1 While I was never a fan of of, I'm willing to live with it. I fully support adding sugar for InlineArray. If we've decided that of is the keyword to use, then [n of T] is the correct sugar, and definitely generalizes to a future [n of x] literal syntax.
I'm also interested in a future (n of x) literal syntax for homogenous tuples, although I expect them to be less frequently used now that we have InlineArray.
If you're making an argument that [5 of Int] isn't consistent with other uses of [...], then I don't think inventing an entirely new form for generic argument clauses out of whole cloth for a specific sugared type where the arguments are written to the left instead of the right is a consistent or realistic option, either.
Imo that's not really a problem because in contrast to C, the nesting square brackets clearly indicate which is the inner and which is the outer arrays count.
Nesting or otherwise, it would be a completely unforced error for readability and education to make [[T]<n>]<m> and InlineArray<m, InlineArray<n, T>> mean the same thing. The choice to position the dimension first in the nominal version was specifically made to ensure that the multidimensional cases read correctly.
As much as I would have liked InlineArray as a concept to be the same thing as a tuple, I don't think supporting the above syntax is a good idea. It encourages creating tuples with a large number of elements, which won't work very well ABI-wise in Swift (creating large symbol names).
The repeat keyword is already used to "expand" things in-place. Perhaps it's better to allow (repeat 3 of Int) to expand to (Int,Int,Int) and (3 of Int) on itself would be (InlineArray<3,Int>). This would further allow things like (repeat 3 of 3) to become (3,3,3).
The repeat keyword really sells that it is literally duplicating in-place.
Personally, I don't think it's desirable to have a literal syntax for every type of array in the standard library. People would need to learn each syntax, even for array types that are rarely used, and types outside of the standard library wouldn't be able to make their own sugar.
Type nesting, on the other hand, doesn't require every type to be memorized and can be done by non-standard libraries. If we want to bring the conciseness benefits of sugar to other collections, I think nested types are a better path to go down than a bunch of literal syntaxes.
let a: [String: Int].Ordered = [1: "one", 2: "two"]
let b: [UIColor].Contiguous = [.red, .orange, .yellow]
let c: [Int].Small<5> = [0, 1, 2]
I'm a bit sad to see x go. My main issue with it was that it wouldn't compose well with a future value version (ie [5 x 3] being an InlineArray of 5 Ints, each of them equal to 3) as it was too easy to confuse with matrix notation (ie a 5x3 matrix), which did not seem like a solvable problem.
And yet, I can't shake the feeling that x was brilliant in a way of isn't. Don't take me wrong, [5 of Int] is not bad at conveying "a collection of 5 Ints", but it's strictly inferior to [5 x Int] at that.
I wonder, if in the quest of finding and discussing all the possible edge cases ([_ x x], [n x x]...) and ways of "holding it wrong", we may have lost sight that what this sugar fundamentally needs to accomplish is a shorthand to declare an array of a fixed number of items, and excel at that.
In any case, I think of is a good compromise given the unresolved issues of x for value sugar, and I'm looking forward to being able to use this. I do think syntax sugar is going to be immensely useful for those that need to reach for InlineArray.
Personally, I'm particularly sad to see the future direction for flattened multidimensional arrays now being [5 of 5 of Int], because [5 x 5 x Int] is much better than any other syntax for multidimensional arrays I've ever worked with, with unmatched clarity when translating from/to mathematical notation. In comparison, [5 of 5 of Int], or the unflattened [5 of [5 of Int]] are... underwhelming.
But oh well, that's still a future direction in any case.
If the alternative is InlineArray<5, Int> I find the proposed syntax to be much worse.
I don’t see any strong reason to introduce shorthand/sugar for this, other than to correct the „mistake“ (which I don’t really see as such) of privileging the plain square bracket syntax for Array. Others have rightly commented that InlineArray is not the only new type that may want syntax along these lines: why InlineArray (only)? I think it’s too soon to make the call if nothing else.
Overall it appears to be just adding more inconsistencies in the language for a minimal gain at best. This kind of addition seems to be increasing complexity in the language, which already has way too many moving parts and „hidden knowledge“ requirements.
I feel that this sugar is necessary. While InlineArray is clearly going to be used sparingly in many places, I think that a lot of people are forgetting how frequently this will be used in performance-critical code and how this must completely replace Array on many embedded platforms that can’t allocate on the heap. In these environments, I don’t have the chance to try the “normal" collections and profile them as this is not possible. While I don’t think InlineArray will ever be the “default” in Swift, it’s clear that it will be used quite frequently.
I do agree that of makes more sense than x because, even though it is a bit longer, it more explicitly states what this actually means and fits better for the value sugar. I think that the [] around it works because it shows that this is a collection. Both the array and the dictionary sugar uses this, and it doesn't make sense to break this convention from many other languages without a very good reason that InlineArray should be treated differently. Using () doesn't make sense because while the memory layout is similar to tuples, the usage isn’t and we shouldn’t be encouraging people to think of InlineArray and tuples similarly.
I don’t like the [Int]<5> syntax because I think it throws away many conventions in Swift and other languages, it just isn’t very readable because it doesn’t explain what the number means, and it puts the size on the wrong side of the element type.
Personally, I think that the possible syntax for flattened, multi-dimensional arrays ([5 of 5 of 5 of Int]) is absolutely unreadable. If we want to use of, there needs to be some separator because it’s not an array with 5 of 5 of 5 of …, it’s 5 of [an array with 5 of [an array with …]]. I think we should either not add it at all, or there should be some other delimiter between elements of the shape and the element type:
I appreciate your point of view here. And I agree. Although I am not often writing optimized code, I have a lot of respect for those who NEED to be writing optimized code using these specialized, constrained types. When reading code that has been meticulously crafted, I am always surprised at the lengths programmers go to ensure speed, low memory usage et cetera. For those of you who are unfamiliar with what I am talking avout, a great starting point would be some of the discussions (or even source code notes) which debate the use of a tuple, struct, class, Copyable and ~Copyable and their performance nature (allocated on the stack vs not, memory footprint, copy performance), and the various ways to avoid certain non-optimized circumstances that are not explicitly exposed to the developer in the API -- parts of the language that you would have to have understanding of the internals to grasp a detailed understanding of.
Although I will likely not use InlineArray much (even in circumstances where I should), those who are confined to performance constraints deserve the attention we give here.
If any of you are heavily active in generating optimized code where you believe that this Sugar is NOT needed, please speak up. I would like to hear from you too.
A part of the discussion I really wanted to draw people's attention to was giving us an idea of how they would be using InlineArray, to help us fully understand THEIR needs. I imagine that Hamish Knight, Ben Cohen both understand this need which is why they are advocating for it.
The syntax let a: 5 of Int could be read by someone not knowing the language as just saying “5 is of type Int”. This could be used as a guideline. The syntax let a: [5 of Int] indicates that there is more to it, and those brackets are common in other programming languages.
let a: [5 of Int] Is not logically correct, as 5 of Int is not the type of the elements, but natural languages are also not “logically correct” sometimes but function quite well. I think [5 of Int] is a nice syntax.
This is a good point. It could be misinterpreted as let a: Int = 5. But it shouldn’t take them long to realize that Swift's variable declaration is always let <identifier>: <type>.
I am not against [] in general, as [] directly implicates indexability. But, it is not intuitive that of is part of [] in [5 of Int]. 5 of Int wants to become a separate expression. Consider expressions like [5 is Int], [5 as Int], all of these mean an array literal of something. It’s almost like we need to use [5, of: Int] to stress that they are together. Perhaps, it’s reasonable to do something like [5|Int], [5,Int], or [5’Int] to give the impression that the separator is part of the [], just like how : seperates dictionary literal [A:B]. 5[Int] could also work.
Same for me. The [N x T] felt so much clearer and provided a better path for future directions.
For me, [7 of 9] does not imply a multiplicity in any way and yes, in [7 x 9] you have to remember in which order to read it, but it's the same in InlineArray<N, T>.
I'm coming more from the embedded and systems programming background and was very fond of the first draft of this proposal.
I have the fear that we choose an inferior syntax, just because that first draft got so much backlash by a few loud people. Now everybody is tired fighting and just wants to move forward again.
So for this version of the proposal, I'm -1. The syntax [N of T] may be shorter but in my view it is inferior to writing the full InlineArray<N, T>.
I think that [N of T] is a very reasonable approach, and appreciate the effort to revise the initial proposal.
What I still struggle with as a non-professional academic tinkerer is tracking when and where CoW applies, since there's little (or no?) indication in the language. When Swift was first introduced CoW was a big feature — we don't need to worry about it, it's built in, and we can take advantage of it whenever we like.
Now that we're moving to performance sensitive code, CoW isn't an advantage, and it makes sense to introduce types that elegantly and concisely help us code in those scenarios. But this also feels like a trap with no warning to others. Why does CoW apply to [N] but not to [N by t], and how can I look at code and understand when and where it's being applied without spending a lot of time digging through the manual.
Part of me wonders if we might want to introduce a small annotation to help distinguish the inline-ness of the literal. But I also have no sense of future directions, and what other cases might benefit from this. e.g., I assume our thinking is that our most common cases are CoW + dynamic sized [N] and non-Cow and fixed size [N by T], but why those specifically?
Will we ever have non-CoW literals for [K: V], or other types? And do we want to consider how we would specify those now?
I know it's outside the scope of this discussion but it's tangental to it: When we do get a pitch for the value literal sugar, should we expect an ExpressibleByInlineArrayLiteral protocol to be inherent in that pitch or would we expect the sugar to be exclusive to InlineArray and "hard coded" to work with it?
Actually, is there a particular reason why the value literal is not part of this Proposal? It seems like the needs of each separate sugar inform the design considerations of the other.
Following the discussion I constantly think about what would this argument mean for a future value literal syntax? How does that proposal fit with an ExpressibleByRepetitionLiteral (as I like to call it).
I‘m not sure the separation of type sugar and value literal really is helpful. It would be great if the design of both would work together intuitively.
On the other hand, it might broaden the discussion too much. There are at least two aspects to the literal to consider:
The repetition literal should be designed so that other types can use it as well:
let inlineArray = [4 x 0]
let array = [4 x 42] as Array
let string = [4 x " "] as String
InlineArray needs to support other literals, too. Repetition is just a special case of initialization.
let zeros = [8 x 0]
let fib = [1,1,2,3,5,8] as InlineArray