SE-0483: `InlineArray` Literal Syntax

Perhaps. I could see the argument for making their multidimensional expression easier than for the normal array being that for use case when multidimensional arrays are desired, they are perhaps more commonly fixed size and populated. Still, I do think the sugaring is likely premature.

1 Like

Why? It is not a "cool" feature, but a standard and fundamental building block that was missing in the language. So it should have an easy, intuitive and concise way for initialization and usage. The arguments against introducing sugar would be:

  1. The syntax is bad and painful/cumbersome to use.
  2. It conflicts with other language features.

None of these are solved by waiting.

4 Likes

What exactly is a "multi-dimensional normal Array", though?

Multidimensional Arrays aren't all that useful for the kinds of computations you'd expect to use a multidimensional structure for, because they're not actually matrix-like in their storage or their shape: they're Arrays of Arrays (of Arrays...), and so there's no locality of the allocations so there's extra indirection required, and the individual sub-arrays can have different counts.

A multidimensional InlineArray is far more useful because it's a single contiguous region of memory and there's a static guarantee that the dimensions are fully populated.

There's room in the middle for a sparse multi-dimensional structure, but that's usually going to be represented with some other specialized layout.

So if we're comparing the utility of the multidimensional forms, having a more compact syntax for InlineArray is far more useful than Array.

13 Likes

The semicolon token `;` in that context really looks strange and feels unnatural. Besides, it is already used to delimit multiple statements on the same line.

let u = x + y; let v = u + 1

Also, can all those souls using the pathological [x x _] example as an argument against using x as proposed start using a different variable instead of x? :slight_smile:

If x is so disliked, it can be elided, without any loss of meaning.

[3 x Int]
[3 x [3 x Int]]

This is just as good:

[3 Int]
[3 [3 Int]]

But, I am hoping and wishing that x stays and is accepted. :crossed_fingers: :crossed_fingers: :crossed_fingers: :slight_smile:

5 Likes

That's fair enough, agree with your view - I think my main question is really whether sugar is warranted at this time at all, but it might simply be that I misjudge how common the use of this type would be. I seem to remember from the pitch thread that it was considered a fairly specialist tool (with performance pitfalls due to the inline nature if used without care).

1 Like

So does & for denoting address (strange and unnatural). It is also used to denote bitwise and. However, it has established meaning in C/C++, hence it was happily copied and didn't cause any confusion. I argue, that ; probably cause even less confusion compared to &. And since it is used in Rust, which is becoming more popular, if the folks are fine with copying C syntax, why not copy some Rust syntax too? The advantage being familiar from other language, not relying on whitespace as in [5 Int] and being as ergonomic. Also [[3 [3 Int]] would be hard to extend to multidimensional arrays.
Again, for "strange and unnatural" - the moment it's decided that [5; Int] means 5 dimensional array of Int, people will get used to it in a couple of hours and forget that it looks "unnatural and strange".

x is a common variable name when writing math heavy code. For example, it usually denotes the name of the first coordinate when dealing with low dimensional vectors established and widely agreed on meaning. Why would one use a different variable name?

TBH - It is better than no syntactic sugar at all, and '[x x _]` will become just another quirk of the language, and people will get used to as well. There are just better options.

4 Likes

This is a big risk that the proposal does not address. It’s super easy to write [1024 x MyStruct], without realizing that copying that value will result in literally thousands of retain/release operations if you have multiple refcounted properties on it. The naming discussion of InlineArray was so long in part because some people wanted to be very explicit about the fact that this type stores a potentially large number of values inline.

Adding this sugar now before any meaningful adoption has happened risks it becoming popular wherever a fixed-size array is desired without people specifically learning about its quirks.

I believe this feature should wait at least for a release or two so the community can get develop best practices for when and where it’s appropriate to use an inline array. Ideally we’d wait until the full set of non-copyable container types is in use in the wild (or at the very least pitched) as well. That way, everyone can gain experience around which container types are common and important enough to need a new dedicated syntax sugar.

21 Likes

I commented the pitch thread that the inline nature of this type would be clearer of it was spelled 8 of Int with no brackets to emphasize the inline nature of the array. I still stand by this position.

(This could work with 8 x Int too, even though I prefer of.)

var x: [Float]      // regular array
var y: 100 of Float // inline array
// or:
var y: 100 x Float  // inline array

Nothing makes it feel more inline than not wrapping it brackets.

Examples with “of”
 // tuple
var z: (100 of Float, 50 of Double)

 // function signature
func foo(components: 16 of Int)

// generic arguments
var s: Set<16 of Int>

// mixed with existing collection sugar:
var d: [String: 16 of Int] // Dictionary<String, 16 of Int>
var a: [16 of Int] // Array<16 of Int>
Examples with “x”
 // tuple
var z: (100 x Float, 50 x Double)

 // function signature
func foo(components: 16 x Int)

// generic arguments
var s: Set<16 x Int>

// mixed with existing collection sugar:
var d: [String: 16 x Int] // Dictionary<String, 16 x Int>
var a: [16 x Int] // Array<16 x Int>
13 Likes

The proposal's Alternatives Considered section only briefly mentions # — which is surprising given the positive reception of posts about # in the pitch thread (1, 2). The proposal groups it with other "arbitrary" symbols:

[5; Int] is what Rust uses, but appears to have little association with "times" or "many". Similarly other arbitrary punctuation e.g. , or / or #.

But it isn't really arbitrary — # does have relevant associations. For example, in set theory, #S denotes the size of a set S. And as previously discussed, it would be compatible with the future value literal variant and source compatible with freestanding macros.

let fiveIntegers: [5 # Int] = .init(repeating: 99)

// type inferred to be [5 # [5 # Int]]
let fiveByFive = [5 # [5 # 99]]
2 Likes

You want me to stop using x as a variable name in all my 2d/3d graphics code because I might want to use an inline array somewhere?

When a solution to a problem needs a bunch of kludges or workarounds to work it generally means it's a bad solution, and I think this is an example.

Why do we want to use an arbitrary symbol when a very short keyword, of, works just as well? What are the reasons people don't want to use of?

13 Likes

I think it is more appropriate to suggest that new features adapt to the current Swift culture and not the other way around.

As discussed using of is significantly better received. And would strengthen that the new Swift feature honors the current users of Swift.

You will notice that a major of the pushback -- it seems to me -- regards x as the keyword.

I know that 'likes' are not a metric that the internal teems use to decide what features should be implemented, but the original post got -- so far-- 8 likes, JuneBash's commentary on the opposition of x got 32 likes. There are a lot of people who are remaining quiet on this, but strongly(at least tentatively) agree that x is not appropriate.

4 Likes

This is a good point and gives it more justification than the other "arbitrary" alternatives like ;. I'll expand on the alternatives considered here. The main issue is that # as you point out is a unary prefix operator, not a binary infix operator. This can probably be made to work, but would lead to a (IMO) less readable outcome because it clashes with other uses such as #5 meaning "number five" not "five in number" – that is, to me at least, [#5 Int] looks like "the number 5 Int" not "five ints".

Of course, you can stick with infix notation, just using this as the "why" for using #. But that seems like the analogy to a similar concept doesn't bring a tangible benefit.

By contrast, [5 x Int] being read as a block of memory of size "five by Int", is directly analogous with real world usage (i.e. a 2x4 block of wood).

4 Likes

This is a great observation and lends credit to the idea that a full English word be used and not a letter (as is the current culture of Swift keywords, that aren't in the Unicode symbol range). I haven't done an analysis of my code (like I did with of), but I think that by might be equally as less used than x as a variable and it reads just as well as of. I suspect that by is often used as an argument label. But I think it would still be an acceptable and superior alternative to x.

4 Likes

I think the problem with the "x is a common variable name, therefore [x x _] will be common" is that this is generally not true. It misses the necessary step that x is common as a value that appears as an array size. What would be needed to strengthen this case against x is evidence that it is common to, say, get an x coordinate and then use that x coordinate to create an x-coordinate-sized array.

Note that this would also be a discussion about the value version. You will never be able to use a dynamic value like an x coordinate in the type version. For it to be relevant to the type sugar, you would need to justify a compile-time constant often be named x (either an integer generic parameter, or in the future maybe a compile-time-calculated named constant).

Now, I think we should in this discussion consider the repeated value future direction too, so it's fine to make the argument let a = [x x value] might come up. But still, it seems a stretch that an x coordinate (or some other common legit use of x, like the unknown in an equation) be used as the size of an array. You might have better luck with the value, i.e. [size x x] could perhaps be more common.

Nevertheless, the point is:

  • this doesn't seem common, as either a size or a value
  • in a lot of cases, x isn't "the x coordinate" but just "no good name for this, eh, let's call it x" where you can call it something else
  • in the seemingly rare case where x genuinely does mean the x coordinate, and you want to use it in part of an array repeated value, you can just:
    • accept it even though [size x x] looks weird
    • not use the array sugar in this case, e.g. .init(repeating: x, count: size)
8 Likes

This is a fantastic point and fully convinces me of [5 x Int]. Conceptualizing InlineArray as a block of memory is a great mental model for that type. Works especially well with types that have their size in the name: var buffer: [1024 x UInt8] literally spells its memory footprint 1024 * 8.

I could see [5 by Int] too. I didn't like of very much because grammatically it's a little awkward, but by is much better in that regard. Personally, I still prefer x visually because of the similarity to the multiplication sign and it being common in everyday written language. And @Ben_Cohen makes a strong case that it's unlikely to conflict with variable names.

1 Like

I am starting to as well. by just reads better than x (literally when you read it out loud). For that reason alone, I would go with by or of.

The argument for me has been less about the compiler and more about the human behind the code. It's well established that none of the chosen keywords would cause an issue for the compiler. The human aspect here is what makes x undesirable.

I would go with of or even by (I am starting to like by).

7 Likes

I, too, would prefer waiting for InlineArray to be introduced to the language and only once we have a better understanding of it’s use decide whether a shorthand syntax is needed, and if so, how that syntax should look like.

There is still too much uncertainty how important multidimensional inline arrays are goring to be, for example. Or how common the variable x is for the array size. Or which domains will adopt the inline arrays most and what existing syntax they might be most familiar with.

I am not opposed to having a shorthand syntax, but for now, choosing among a set of (to me) bad options just so we’ve picked one feels premature.

9 Likes

“five by Int” does not sound natural to me. “2 by 4 block of wood”? Sure. But “2 by block of wood” or “2 by 4 by block of wood” or “2 by wood”? No.

10 Likes

This is a good argument for why of might be better than by and better than x. And again both are better, than bare x. Good insight.

3 Likes

I would not call ; syntax arbitrary. The fact that Rust uses it makes it not. Rust is on the rice now as "go to systems language", and using familiar syntax (and it plausible to assume that Rust might get more adoption as systems programming language instead of C in near future) would make Swift adoption easier. It also has many benefits of the propopsed [5 Int] syntax, while being less "white space dependent". It also has a benefit of not being alphanumeric and less likely to confuse with an identifier.
Again it is not more arbitrary than @ and # for macros, & for address, . for membership notation and so on. These are common symbols used in mainstream programming languages. ; has a benefit of being used in a mainstream programming language that is similar to Swift.

of and by suffer from similar drawbacks as 'x' - namely being valid identifiers. I believe to be good solutions, they'd also need something like preceeding : (:of instead of of).

2 Likes