It’s true that this is uncommon, mostly because the traditional US keyboard layout has so many symbols on it already. I can think of at least one direct precedent for it, though: SML’s o
operator is clearly an ASCII-ification of the standard mathematical syntax ∘. And there are quite a few languages with infix keyword operators in general, like JavaScript’s in
or Java’s instanceof
.
Heck, even Swift's is
and as
keywords are for all intents and purposes operators in the language. They're not spelled with "operator code points" but they do participate in the same precedence rules as all the other operators.
So there may be other reasons to disprefer it, but I don't find the argument that x
would be unique in this regard compelling on its own.
Sure, but I don't think most of the folks bristling at x
object to the general idea of keywords-as-operators—based on the feedback here many people who reject x
would not have an issue with by
, of
, or similar. I think that while not entirely unprecedented, it is definitely a difference-in-kind to use a letter character as an operator purely due to its typographical similarity with the target operator (though it's not a difference I find to be that worrisome). Operator keywords which are semantically meaningful are not attempting to visually imitate some other operation, they're just functioning as the words themselves!
I think there's one thing worth noting from this discussion, which is that, to a good approximation, there have been essentially no new ideas for syntax in the intervening decade, which is strong evidence that delaying introducing sugar is not going to magically result in "better" sugar.
I think one can argue about whether or not sugar is needed, but if it's decided that we should have it, there's nothing to gain from deferring adding it.
Glad you asked. I hope we can clarify this then.
I am going to assume, then, that 4xInt
≠ "4 x Int" or "[4 x Int]". Despite this the x
is still there. The order is still there.
Since the x
is there, I can assume that the Core Team likes x
and have concluded that it should be a part of the syntax. (this isn't a far reach).
The order being similar suggests that the order is also preferred. (this is not a far reach)
I doubt anyone would agree to #xInt
being hardcoded into the language, so we can at least conclude that that would be up for debate.
I can tell that some of us are more literal thinkers than others, where any deviation from accepted syntax (10 years ago) would not at all be this one because it is slightly different. I can often be like that myself. But in this case, can we not see the parallels between the suggest 4xInt
syntax and the proposed [4 x Int]
a good amount of which you admit the Core Team already prefers.
Unless you are suggesting that the Core Team only would prefer if it is all elements were the same (therefore 4xInt
is absolutely not [4 x Int]
), and since this deviates with this with the aded []
and spaces they would not tend towards any of the suggest syntax in this proposal. Which, OK, I can agree to that.
Looking through Proposal: Contiguous Variables (A.K.A. Fixed Sized Array Type), I can see so much of the same discussion.
(# x Type) was suggested.
(# * Type) was suggested.
(# of Type) was suggested. (ADDED AT EDIT)
(# Int) was suggested. (ADDED AT EDIT)
Int[#] was also suggested.
(lots of similar arguments)
I didn't read through all of it, but it didn't feel like the conclusion was #xInt
or #xType
.
I am interested where that came from.
Regardless, I feel if the Core Team has decided on a syntax that they prefer and a lot of the elements are here, then our debate on those portions are irrelevant.
why does such a niche feature get its own special literal syntax? this seems like a really bad precedent for the language, the core team, and the community at whole.
Please don't use non-ASCII symbols like ✕
. I've had to deal with a lot of python, and having to deal with symbols that look the same but aren't the same (eg. spaces vs tabs in python) is an absolute pain.
Magically replacing "x" with that symbol is also less than desirable, since that means that you can't really write it easily with non-IDE editors (eg. Sublime text, which I sometimes use to edit individual swift files when I don't feel like booting up Xcode in full). Expecting people to input its raw unicode is unrealistic.
==== end of semi-rant ====
From what I can see, the concerns people have are:
- Clarity + accuracy (can I tell at a glance that this is an InlineArray)
- Brevity (is it shorter than typing out
InlineArray<10, Int>
or nested IAs for a matrix) - Anti-footgun-ness (will a beginner accidentally use this without realizing the performance detriment of creating a million structs in inline storage)
- "Seriousness"/swiftyness (does this feel like it belongs in swift?)
- Precedence (would it lead nicely to other collection-like sugars in the future)
In my opinion, [N inline T]
(all but point 2) is the best I've seen in this thread. I would like to propose N[inline T]
, which is that one merged with the N[T]
idea (all but 3 and 5). Reviewing it with those points:
- Clarity: This actually reads quite nicely in English ("10 inline integer[s]"). It also gets across that this is an inline array, by literally having it in the sugar
- Brevity:
N[inline T]
is 6 characters shorter than its unsugared form, though it is still about 2/3 the length. - Anti-footgun-ness: This syntax is far away enough from Array that I don't think it will be seen as a drop-in substitution. Unlike
N[T]
, it doesn't feel like "just" a fixed-length array. - "Seriousness": No magic here, no reading "x" as "by" (I've lived in cities my whole life - 2x4 means nothing to me), and it doesn't conflict with existing subscript syntax due to the "inline".
- Precedence: Like people before me have said, the "inline" part could be replaced for other types, giving us a very standard way of producing future sugars (eg.
[deque T]
,[ordered K:V]
?)
TL;DR: N[inline T]
(to me) is clear at a glance, short enough, distinct from Array, feels most swifty, and leaves a very clear precedence for potential future sugars.
BTW, this is how other languages do it:
Language | Declaration | Usage | Expression |
---|---|---|---|
C | int x[1][2][3]; | x[0][1][2] = 42; | {{{1,2,3}, {4,5,6}}} |
D | int[3][2][1] x; | x[0][1][2] = 42; | [[[1,2,3], [4,5,6]]] |
Go | var x [1][2][3]int | x[0][1][2] = 42 | [1][2][3]int{{{1,2,3}, {4,5,6}}} |
Rust | let x: [[[i32;3];2];1]; | x[0][1][2] = 42; | [[[1,2,3], [4,5,6]]] |
Pascal | array[0..0,0..1,0..2] of Integer; | x[0,1,2] := 42; | (((1,2,3), (4,5,6))) |
I like Go version most: declaration order matches use order and it reads left to right logically: "array of one array of two array of three int" (in C you have to jump back once you are after "... array of three" to get to the element type). It also mimics the usage which is important in C based languages (not important in Swift).
In this form it won't be possible using it for values (of non inline arrays), which we might (or might not) want. Example:
let x: [Int] = [10 x 42] // 😐 "That's 420, right?"
let x: [Int] = [10 42] // 🤔 "An array of two elements with missing comma?"
let x: [Int] = [10 inline 42] // 😒
let x: [Int] = [10 by 42] // 😒 "Are you talking about your room size?"
let x: [Int] = [10 of 42] // 😀
let x: [Int] = [10]42 // 🤔 "no idea"
let x: [Int] = 10[42] // 🤔 "Do you have a custom subscript defined on Int?"
You left out the granddaddy (and by far the best) of them all:
integer dimension(3,2,10) :: x
sugar:
integer :: x(3,2,10)
custom start and endIndex per-dimension:
integer :: x(0:2,-4:-3,1:10)
scalars:
x(2,-3,1)
slice odd-indexed sub-matrices in reverse order:
x(:,:,9:1:-2)
Despite the name of the proposal, this isn't a review of a literal syntax for InlineArray
values but of sugar for the InlineArray
type declaration. I refrained from commenting on the topic, but since it keeps coming I'd like to point out a couple of things:
-
As far as I can tell, the intent has always been that the literal syntax for
InlineArray
would be the same asArray
: a bracketed list like[1, 2, 3, 4]
. Whats gets discussed here as "100 x 42" would be better described as a "repeated element literal". -
In general, it's wasteful to store a "100 x 42" value as an inline array. I think the appropriate default type for such a literal should be a type like
Repeated
—perhaps one with a compile timecount
—that won't be storing 100 identical values needlessly. If you want to initialize an array with that literal that's perfectly fine, but you should have to tell the compiler you actually want an array, inline or not, either explicitly or via type coercion. -
I don't think the repeated element literal necessarily has to ressemble the inline array type sugar syntax. One is for declaring an array type, the other for declaring a repeated element. They could be alike since they both have a "count", but they're fundamentally not the same thing, in my opinion.
So that concludes my thoughts about the "repeated element literal": it should not be tied to InlineArray
at all and doesn't need to use the same syntax. Also, it's not really part of this review.
Well, we may end up with:
let fiveIntegers: [5 of Int] = [_ of 99]
instead of the proposed:
let fiveIntegers: [5 x Int] = .init(repeating: 99)
as a result of this review, so it makes sense to discuss types and values holistically.
I think the close it can get without a syntax too different from what exists is [Int]<2>
. I think it reads well like "An inline array of integers, constrained by 2 elements". More dimensions could be represented by [Int]<2><2>
or [Int]<2, 2>
.
I have thought about this and was just wondering if this would force any slowdown in parsing. It seems like it would add a lot more look ahead.
Otherwise, I feel like this has some of merit.
Oh I was only suggesting N[inline T] as a type signature sugar, not a .init(repeating: N) replacement.
I don’t actually see why we need a dedicated inline array literal, esp when init repeating exists and has existed for quite a while.
To be honest, the big issue with x
is that it is a valid identifier. It also suffers a bit from NIH syndrome (we have another mainstream language similar to Swift, Rust, which has an established and familiar syntax [5; Int]
, why not just adopt it?), but except these two issues it is fine. It looks like \times
symbol in TeX and reads the same. [5 x Int]
means "5 times Int" which is pretty clear description of an inline array. So from readability point of view, it is not worse than of
or by
, and not worse than the whitespace syntax [5 Int]
that many people loved, and actually better if you are willing to accept that x
reads as "times". It has other advantage over by
and of
by being shorter and by resembling established mathematical notation. (Since +
means concatenation, *
or \times
(aka x
) is a commonly used notation to describe repeated concatenation. Maybe calling x
operator \times
and have autocomplete automatically convert it to the proper ×
symbol would make some sense, but x
requires less typing and looks the almost the same.
It also has the advantage of being easily extended to define multidimensional arrays (when they are treated differently from nested arrays). Other proposed syntax forms (including ;
) don't have this advantage, unless we adopt tuples as valid generic arguments.
This comes up a lot, so to expand on the rationale from the proposal: there is no good basis for ;
as the separator here. It doesn't convey meaning unlike x
or of
or *
, and isn't precedented like ,
is for separating generic arguments. From my perspective, Rust made a mistake here and not one we want to repeat. @xwu in the pitch helpfully located the Rust RFC, but unfortunately it does not actually detail the rationale for the choice very much.
Swift does carry forward long established C conventions (like &
for inout
arguments, ?
for ternary expressions). But this is something reserved for C, with its many-decades-long history and syntax heritage passed on to many other languages including Swift. Aping other contemporary languages isn't a goal. Of course, if another language makes a good choice, that's great inspiration. But there seems no actual argument to repurposing ;
.
Furthermore, as I mentioned in the pitch thread, [n; Element]
would look inconveniently similar to existing dictionary type sugar and could be misread as [n: Element]
in quick scans of code. Having two forms of type sugar distinguished only by a dangly bit at the bottom of a punctuation mark doesn't strike me as user-friendly syntax design.
Literal numbers aren't the only thing that might go into that position. I was using n
as an identifier, since another way this can be used is with integer generic arguments (or later on, compile-time constants). The only thing separating [Thing: Other]
and [thing; Other]
is case convention and a few extra pixels.
If we were going to create a new syntax that is so similar to an existing but unrelated syntax, we'd need a better reason than "Rust does it (but doesn't explain their rationale)".
Doesn't a commonly used case convention make it clear enough that n
is not a type? I've yet to see people confusing ;
and :
in general.
I don't know their rationale, but I think using a rarely used symbol (I don't think ;
is often used in Swift code to seperate statements/expressions on the same line) as a separator between 5
and Int
in [5 Int]
instead of relying on whitespace is not a bad idea.
Anyway, after giving it more thought, I do believe that ;
doesn't have many advantages over x
except not being confused with an identifier (but Swift already has some keywords that depend on context that can be used as identifiers or parameter names in some cases, it's not really an issue I believe, and any editor/IDE would use distinct color for x
anyway, making it even harder to confuse the identifier x
with the keyword x
), not suffering from NIH syndrome (but may be ;
was a mistake as @Ben_Cohen suggests) and not requiring whitespace around it. If these are not strong enough cases for ;
, x
is probably better. (And since it reads as "5 times Int" it is more clear than "5 of Int" or "5 by Int" that have been suggested before).
I do think that the other sentiment often stated here - are [ ]
around 5 x Int
(or 5 of Int
) really necessary? The expression 5 x Int
is "cleaner" that [5 x Int]
- but perhaps it would make parsing harder?
(Actually, 5 of Int
etc, would require braces around it every time one invokes a constructor, making it as "clean" as the typealias with square braces, so []
should probably stay as well).
So, +1 for the suggested syntax if some syntax for InlineArray
gets adopted (but if there is a necessity to adopt InlineArray
syntactic sugar, there's no point waiting).
There are a few questions that require closer attention.
- Is it really needed? (I am in the camp that believes that it does, but
InlineArray<n, T>
is not that bad). - Are there plans to introduce syntax for
InlineArray
expressions that is different from the one forArray
expressions? Namely not plain[...]
? It makes sense both from conceptual point of view - as these are different entities from "regular" arrays after all, and to have pity on the type checker - having it infer the type of nested or multidimensional arrays would probably cause it have an heart attack - it already struggles with inferring numerical types in long expressions.