SE-0483: `InlineArray` Literal Syntax

I will admit that I am currently only at about comment 92 out of 223 fully reading, but have scanned a bunch of the rest. I'll keep going.

But so far my take away is that a LOT of people do not understand InlineArray. Generally there is a cohort that thinks it's a fixed size Array. They think there are no potential negative consequence for picking it instead when their current code uses an Array to hold only one quantity of a Type. There is a subset of people here vigorously discussing this sugar while not understanding the memory allocation pros and cons of the different types. Obviously not everyone and obviously not the person who pitched it.

The nice thing about the name Vector is that no one would have made that assumption. It would have felt more alien. And appropriately so. They would have taken time to understand instead of the inherent chumminess of something with the word Array in the name. So familiar! So easy! Vector had other problems, so it didn't win out. But the fact that it othered itself from the Array family wasn't one of them to me.

Introducing syntactic sugar that makes this type easy to grab and frankly makes it feel even MORE like a fixed-size Array would be a mistake to do before the actual Fixed Size Array makes its own possible debut.

Side stepping the x, of, by, ; issue for a moment because for me the problem starts with the [. (I'm team , FWIW)

If we're going to be saddled with sugar (prematurely imho) why not draw upon the other thing that is stack allocated, the Tuple right? I could be the one misunderstanding, but in its implementation doesn't InlineArray have more in common with a homogeneous Tuple than an array that is only ever one size? I think the sugar should reflect that unless that distinction is actually trying to be hidden.

So in that line why not use myvar:(Int, 5)? (I still like the type forward way it echos .init(repeating:count:) nicely. Why not use a (...) as the literal?

EDIT: Doh. Missed it in the alternatives
ETA: For multidimensional ((Int, 5), 3) is the same syntax because (Int, 5) is the type. (5, Int) --> (3, (5,Int)) would be the flipped version.

9 Likes

Tuple <3,Int> feels natural , but the sugar (3,Int) feels a bit outlandish. :slight_smile:

However, (3 x Int) and Tuple <3,Int> feels in perfect harmony.

2 Likes

I'm fairly sympathetic to this viewpoint, but given that the LSG has decided that InlineArray has array-nature (hence the name), the type sugar should probably have array-nature, too.

7 Likes

Is that what was decided? Or was it just that Vector would collide with too many other uses and making up some other flibergibity name seemed wrong?

I'll go re-read.

They did go with Inline to really drive home the allocation difference...

ETA: After all they could have just gone with Array<3, Int> as the type so there is meant to be at least some acknowledgement of the distinction.

There is also supposed to be a family of Inlines...

Perhaps they should all get the () vs [] treatment?

Double-negative confusion not withstanding, yes, the naming decision was pretty clear that the LSG decided that this is an "array":

The Language Steering Group did not feel that it was inappropriate for users to conceptualize this as an 'array' type in a colloquial sense. We agreed with the argument that 'fixed-size array' accurately captured much of the behavior of this type, even considering its relationship with the potential 'array' types that we may want to support in the future.

([Accepted with modifications] SE-0453: InlineArray (formerly: Vector, a fixed-size array))

3 Likes

I’m personally both team no brackets and team of.

The brackets serve no purpose.

I also keep getting back to how InlineArray feels kinda like Tuples with repeating fields of the same type. Making me feel like their literal could be a tuple with no labels.

var buffer: 1024 of UInt8 = (0,1,2,3 …. N)

As a side note, I keep coming back to of because it reads in my head as “1024 [Units] of UInt8”

1 Like

1024 of UInt8?

I asked Perplexity the question.

Is there a difference in meaning between the phrase 7 of apples and the phrase 7 apples?

Yes, there is a difference in meaning between "7 of apples" and "7 apples":

1. "7 apples"

  • Meaning: This phrase simply means seven individual apples.
  • Usage: It is the standard and correct way to specify a quantity of apples in English.
  • Example:
    • "I bought 7 apples at the store."

2. "7 of apples"

  • Meaning: This phrase is generally not standard English and is rarely used by itself.
  • Possible Contexts:
    • It might appear in certain specific contexts, such as:
      • When referring to a selection from a larger group:
        • "I picked 7 of the apples from the basket."
          (Here, "of the apples" specifies which apples you are talking about.)
      • In some card games or symbolic uses (e.g., "the 7 of apples" as a playing card, similar to "7 of hearts").
  • Incorrect Usage:
    • By itself, "7 of apples" does not make sense as a way to count apples.

I think the whole passage goes further into it.

Nonetheless, this type does differ from the vanilla Array type in ways which we felt were important to communicate in the name of the type. Top of mind for the Language Steering Group was the behavior of this type regarding copies: namely, that this type is eagerly copied rather than being copy-on-write which carries substantial performance implications. The Group felt that the Inline- prefix captured the salient aspects of how this type's storage is organized.

Many reviewers felt that FixedSizeArray was the most obvious and straightforward name for this type, and the Group was sympathetic to this argument. However, we did not feel this name (or its variants such as FixedArray) sufficiently called out the potential performance issues discussed in the prior paragraph (one could, of course, have a fixed-size array with out-of-line storage and copy-on-write behavior more like Array). Moreover, the Group feels the fixed-size nature of this array will be sufficiently apparent from both from the fact that the generic countparameter appears in the type signature, and from the fact that one will get a compiler error as soon as there's any attempt to actually use a size changing operation—the fixed-size property of this type is not something it's really possible to meaningfully 'get wrong' compared to its performance characteristics.

The Group also discussed alternative, pithy names for the type that could become a new term of art, but in the end did not find a compelling solution along those lines. In particular, each option seemed like it would invite just as much (if not more) clarification than Vector along the lines of "NewTermOfArt , an inline fixed-sized array."

The whole passage seems to me to imply that the distinction was still important enough to make in the name, but no Arrayless names were compelling or free of baggage.

Could be that for sugar a distinction is also desirable like in the name?

1 Like

If we did want to give readers a good intuition of the performance tradeoffs using the sugaring, I do think (n x T) does a good job of that, although use of brackets to index into it instead of .n might be a bit unintuitive.

I still think of is too cute for it's own good though.

1 Like

I strongly dislike the ’[5 x Int]’ syntax. It feels wrong to me.

UPD: even more, I don't think the specialized "inline" type deserves any syntax sugar at all. It's a niche type system that is not intended for 80% of problems that swift as a language solves. I would argue that "borrowing" and "consuming" modifiers deserve syntax sugar more than inline types.

4 Likes

Hello, @PALKOVNIK

Let me try to convince you otherwise.

  1. [...] is associated, in C and C++ , with declaring an array or indexing an array.

  2. 5 x Int means five integers, where x is only a visual indicator, which is not even needed.

Therefore, [5 x Int] (or [5 Int]) is a sweet way of saying InlineArray <5, Int> - an array of five integers stored inline.

Because Swift does not want to sever ties with those languages, the author has rightly chosen the proposed syntax.

There is nothing wrong with that syntax; it does the job quite nicely. :slight_smile:

As a linguists I emphasize with our continuing debate about the "Englishness" of some Swift syntax. I have settled on the reality that we can't really accommodate that entirely. For one, Swift doesn't have plural markers, all nouns in Swift are their own plural. But also, we can't be expected to type in ALL the Human syntax into Swift (à la AppleScript, as fun as that is.)

We've identified veraciously that x lacks the verbosity that we'd like to see in the syntax, but we also acknowledge that by and of require a little more to make them fit into English expectations. Which shouldn't entirely be the bar.

In my previous post, I noted the following

For those of us imagining of being the proper keyword, we are mentally eliding "Units"

With by we're doing something similar.

1024 by [1 Unit of] UInt8

or

1024 by [1] UInt8

It has been stated several times that x is supposed to be read as by, so it suffers the same linguistics problems as the word by, it just happens to be less descriptive.

I don't suppose we'd ever want the syntax to be

1024 unitsOf UInt8 (I would actually support something like this) but it lacks the brevity that we're looking for. Especially in 10 unitsOf (1024 unitsOf UInt8)

An argument for x that I am willing to accept to some degree, is that because it is just a letter having no real English language equivalent and an agreed reading among speakers (I for example will not be reading it as the word by despite that as being the suggested reading of it), it does allow the ability for it to simply mean what we want it to mean -- despite it being less obvious to new readers of Swift as by or of. For me x lacks discoverability. It would be something I would just ignore because it looks too much like a commonly used variable. If I wasn't a part of these threads, I might ignore it longer than needed. Whereas with by and of I may know sooner what it means regardless of looking it up.

2 Likes

When I see [1024 of UInt8], I'm eliding the word "type"—that is "1024 of type UInt8".

3 Likes

[1024 个 UInt8], clearly :sweat_smile:

2 Likes

That is interesting and also kinda fits well with what already happens in Swift.

func declare<T>(_ size: Int, of type: T)

We would call it declare(1024, of: T). We don't restate the word type here.

If we made it more English-like we'd have made it func declare<T>(_ size: Int, ofType: T), which may be seen as not Swifty.

This helps support the idea that we don't need our Syntax to be precisely English but rather just enough to guide the reader and new learners.

4 Likes

Clearly, any measure word should be permitted syntactically, and we can introduce an attribute to decorate types that take a different one than 个. Failure to use the correct measure word will be a warning until the next major release of Swift, at which point it will be a hard error.

6 Likes

It also opens up a possible future direction for a non-empty array, which clearly should have the sugar [some of UInt8]. This can be disambiguated from an opaque element type, which can also be supported in composition: [some of some BinaryInteger].

3 Likes

This makes sense so we could then express

func build() -> (some of some BinaryInteger) {}

or as you may prefer with [ ]

func build() -> [some of some BinaryInteger] {}

and maybe even

func build() -> (any of any BinaryInteger) {}

All of which reads (IMO) better than

func build() -> (some x some BinaryInteger) {}
2 Likes

Generally, the of operator seems to be not only very clear in its intention, but also seems to open the doors to widely reusable and very expressive syntactic meta-feature for expressing repetitions that would be applicable to many use cases. Because at face value, this operator doesn't fundamentally require the use of brackets, these use cases would feel like a very natural progression of a the syntax sugar being proposed here. This is just me fantasizing, so if any of this becomes appealing enough to be put into practice, I'm sure it will be heavily refined.

One could imagine the following uses of this syntactic repetition meta-feature:

  • Putting a syntactic repetition into brackets in a type position forms an InlineArray type (what is being proposed here).
  • Using a syntactic repetition in a place where a comma-separated value list is expected would result in the same effect as if the repeated value was explicitly stated the appropriate number of times: let a: [5 of Int] = [1, 3 of 10, 3] // equivalent to [1, 10, 10, 10, 3]
  • Using a syntactic repetition in a place where a comma-separated type list is expected would result in the same effect as if the repeated type was explicitly stated the appropriate number of times: let tuple: (Int, 3 of String, Bool) // equivalent to (Int, String, String, String, Bool)
  • I'm sure there are more use cases for such a syntactic feature that I can't think of off top of my head.

EDIT:

Coincidentally, the way C arrays are imported into Swift right now could stay exactly the same (at least at first) to preserve compatibility, but they would be spelled in a way that would make them viable to be spelled out in Swift:

const char path_buffer[MAX_PATH];

becomes:

let path_buffer: (MAX_PATH of CChar)
3 Likes

How about

[UInt8 inline ..<100] // Integer literal in range expression equals array size
  • It starts with [UInt8 which clearly indicates an array of UInt8
  • It continues with the keyword inline - which clearly indicates that the array is inline, and we don't need to invent a new keyword.
  • Then goes a range of indices, which quite clearly indicate the array size. When range occurs in the context of an array, index range immediately comes to mind (unlike when just an integer literal is seen - it does not immediately mean size).
  • The whole expression goes inside the square brackets (no hanging parts outside of them), making it clear that we are describing only one thing, namely an array.
  • No commas or semicolons that create visual clutter.