InlineArray
is definitely a collection, it's just not a Collection
.
(I've emphasised the second bullet point of the review 'format', as that is my main point)
What is your evaluation of the proposal?
-1. I like of
better than x
, but my feedback isn't about the quality of the proposed feature per se, but more about the necessity of it.
Is the problem being addressed significant enough to warrant a change to Swift?
Not at this time.
In particular this review calls for use cases for this feature. Here is how I expect to be using this feature and why having sugar syntax is not required for me at this time.
- When I have the need for a collection (as in its behaviour) I typically create one of the 'vanilla' collections as a starting point (
Array
/Dictionary
) depending on how I want to interact with the collection - Wrap the collection in its own type (Object Calisthenics rule #4)
- Only based on performance requirements and after profiling would I opt for more specialised collection types. In other words, only when I have a performance problem, CoW seems to be the culprit and inline storage might provide a solution, would I refactor my type to use an
InlineArray
instead of the regularArray
.
Note that by that time the array type used has become an implementation detail. And writing the full type annotation is negligible in impact in the grander scheme of things.
Does this proposal fit well with the feel and direction of Swift?
I don't have any comments on this.
If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
I've used other C type languages that use fixed size arrays. Typically they have a concise way of describing variables of these types. In that sense you could say that supports the need for sugar here. However, I should also note that in those languages (C/C++/C#) the fixed size array is typically the 'default array'.
How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
I participated in the pitch, previous review and read through all the comments.
+1. It is much better than x
which suffered from being neither an operator nor a sensible keyword. of
is clearly a keyword and the best one available given the limitations of English.
Other languages have words for this. Chinese e.g. uses classifiers like äžȘ which go between the number and the noun. In English though we indicate plurals with a plural noun. Usually by appending "s" but some noun plurals are irregular. "three cars", "three sheep", "three women".
But some nouns do not have a plural, and for those English uses "of": "three of me", "three of those", And this is similar; it makes no sense to write [3 Ints]
, as appending s
to Int
makes it a different unrelated type. Without that available of
makes sense.
I'd like to second this. The brackets are unnecessary and make the type feel like it has a level of indirection (like Array
has), whereas 5 of Int
without brackets feels like you're just stuffing 5 Int
s in place, thus inline. At least, that's how I see it.
No brackets also means the "Flattened multi-dimensional arrays" future direction is already fulfilled: 5 of 5 of Int
works naturally if brackets are not part of the syntax; no special exception needed.
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
.
An element of [(k: K, v: V)]
is not K: V
.
An element of [K: V]
or KeyValuePairs<K, V>
is K: V
.
K: V
is not just a type alias to a tuple.
The difference can't be represented in the type system. The only place it exists in Swift is in Dictionary
type sugar, and ExpressibleByDictionaryLiteral values.
This would come more into play with of
in a hypothetical InlineDictionary
literal.
[5 of String: Int] // 5 "String: Int"s
[5 of (String, Int)] // 5 (String, Int)s
I could see it working as long as [? of Int]
is added to the language and [Int]
is redefined as shorthand for that. (I still think that's confusing, but you can't get rid of [Int]
now.) [Int]
looks like it ought to be shorthand for [1 of Int]
.
[1] as [1 of Int]
[1] as [Int]
[1, 2] as [2 of Int]
[1, 2] as [Int]
[1, 2] as [? of Int] // This is clearer, if we have to form a mental model for `of`.
<5>[Int]
<10>[<5>[Int]]
is better. In general (because most types are nouns), bracketed stuff reads more clearly over there on the left, so I don't know why everybody does it the hard way.
<Bool>Array
<Int>Set
<String: Any>Dictionary
[Int]<10, 5>
was previously proposed. I think it's good, but doesn't scale past multidimensional InlineArray
s.
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.
Good point. I would be OK with limiting it to, say, 16 or some arbitrary arity for tuples if that were the case.
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 Int
s, 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 Int
s", 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.
In short: -1
+ 1
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:
[5,5,5 of Int]
// or
[(5, 5, 5) of Int]
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.
If it wasn't clear, I said the brackets should go on the left of everything, not just <5>[Int]
. If it can only be done with integer generic parameters, at this point, so long after the wrong decision was made, that's still really good.
<10>InlineSet<String>
<4, 4>Matrix<<16>Float>