I used a comma for my proposals to separate the extents.
Moderator note: there was a discussion about the LSG's decision to bring this proposal to review which I have moved to a separate thread. Unfortunately, I moved some posts that I didn't mean to, and this action cannot currently be fully undone in Discourse: those posts have been returned to this thread, but they no longer appear in their proper sequence. I apologize for any resulting confusion.
If we're going to steal the Fortran method of defining arrays, can we at least do like Dartmouth BASIC did and shorten it to dim
?
I've seen some people here propose [n T]
(with no separator) as the syntax we should use. However, I don't think this syntax would be viable. Swift has long avoided putting user-defined identifiers next to other syntax in order to allow for the future addition of contextual keywords.
Imagine, in the future, we added a nocopy
contextual keyword for types. (I am not advocating for or against a keyword like this; just using it as an example.) If we had the [n T]
syntax for inline arrays, then that would be a source break since [nocopy T]
would turn from an inline array into an array of nocopy T
.
Another drawback of this syntax is that with no separator, it would be hard for a programmer to look up information about this syntax means if they are unfamiliar with it. With a separator, they'll be able to include it in web searches to get better results.
I've found this precedent of LVVM quite convincing. This kind of prior art makes a good point because it shows that the proposal is not esoteric as someone may think.
Having followed this whole discussion I think my opinion is basically settling on the following:
- I'm mostly ambivalent about the need for type sugar here. I see how it makes things look nicer (especially for multidimensional cases) but I am also sympathetic to the concerns about the removal of 'inline-ness' from the source-visible surface.
- I think I'm mildly negative on extension of this whatever sugar we would pick to the expression versionāI think
.init(repeating:count:)
is extremely readable in source, and.init(repeating:)
when the count can be inferred is even more concise. I'm not all that convinced that frequency of use of.init(repeating:)
alone is an argument that it needs special sugar. I would certainly want any sugar to be at least as clear as the initializer version. - I think the
x
separator is basically unworkable for the value version of this syntax because[4 x 5]
(or[4 by 5]
) seems to more closely describe a pair of dimensions rather than a description of a dimension and a value to repeat. I think this form fails pretty miserably on the point above of being at least as clear as.init(repeating:count:)
. - If we're willing to diverge on the syntax for the value form here (or accept that sugar for the type form alone is enough) then I think
x
is basically fine, though I'd seriously start to question whether we ought to just use*
at that point. The proposal's argument about[5 * N * Int]
vs[5 * N x Int]
is moderately compelling to me, but (assuming there's no formal ambiguity here) I'm not sure it's quite worth the introduction of a slightly different way to indicate multiplication in type position. Perhaps a more convincing objection in this arena is that this would foreclose having both dimension expressions and a concise multidimensional syntax like[5 * 5 * Int]
for[5 * [5 * Int]
, or else force the parenthesization of dimension expressions as[(5 * N) * Int]
- I am not all that compelled by arguments that this isn't really multiplication in a type theory sense (as opposed to exponentiation. If existing types were more purist about this I'd maybe find it more convincing, but I think there's a perfectly reasonable, colloquial understanding where mutiplying one
Int
by five means you have fiveInt
s.
In that universe, I would spell it [(5,5) * Int]
(there's a quibble here about overriding the normal meaning of ,
but them's the breaks).
Right, I think there are plenty of other ways we could square that circle, but I do recognize that one benefit of choosing a dedicated character for the 'dimension separator' is that in these future directions it ends up composing perhaps a bit more naturally.
With the caveat that this proposal is specifically about sugaring the InlineArray concrete type, and explicitly defers multidimensional InlineArrays to a future direction, if multidimensional considerations are going to weigh on the LSGās evaluation of this proposal, I would ask it be done in the context of a more generic solution that can be leveraged by wrapper types. In particular, I am interested in the preserving the ability to sugar Matrix<let rows: Int, let columns: Int>
such that it does not feel disadvantaged relative to InlineArray<InlineArray<rows>, columns>
.
Tangent, as you acknowledge, but: a matrix is not just a two-dimensional array. I would expect any serious Matrix type to wrap a multidimensional array, rather than be one, so there wouldn't be a disadvantage either way.
Thereās a lot of C/C++ code out there that does typedef struct { float m00, m10, m20, m01, m11, m21, m02, m12, m22; } mat3x3;
and works well enough. Serialization code, especially, doesnāt need to worry as much about actually performing matrix operations as it does about their layout in memory.
Again, this is all just context in case the LSG decides to pull future directions into scope when evaluating this particular proposal.
I think that many of the alternatives discussed here would end up being fine, especially for the InlineArray
type sugar, e.g., [4 x Int]
, but we should also consider the potential future literal syntax, where things may be a bit more unclear for some of the alternatives, e.g., [4 x 5]
.
[4 x Int]
and[4 x 5]
[4 of Int]
and[4 of 5]
[4; Int]
and[4; 5]
[4 Int]
and[4 5]
- etc.
Out of these, only [4 x 5]
and [4 of 5]
seem reasonable to me; with of
preferred over x
, as there is no risk of confusing the order of the arguments: "Is it four 5s or five 4s?"
There was a comparison with the size specification of wooden boards, but I'm not sure if I fully agree with that. It's true that we may refer to a 4 x 5
plank, but Int
is not a dimension, it is more like the wood of the plank I suppose, and we don't say 4 x 5 x Oak
.
Which brings me to multidimensional inline arrays. Jumping the gun a bit here perhaps, but it seems appropriate to explore what an extension to more dimensions would look like.
[4 x 8 x Int]
looks fine.[4 of 8 of Int]
looks terrible.[4 by 8 of Int]
looks good. Too cute?[4 x 8 of Int]
also looks good.
Regardless of what we end up with, I am super happy to see inline arrays coming to Swift. It's something I have been waiting for since version 1.0 and I'm glad to see a natural sugar syntax for it as well, and equally glad that we might see a literal syntax as well in the future.
Will this be required to be on one line? I know Iāve seen the idea thrown around of no longer requiring commas on multiline arrays, and with both that and this, the following code becomes ambiguous:
let foo = [
a
x
B
].self
The title of this review is SE-0483: InlineArray Literal Syntax.
I read that as the Syntax
of the Literal
for InlineArray
.
However, the discussion seems to be almost entirely focused on the syntax
of the syntactic sugar
for the InlineArray
itself.
I thought we were supposed to be discussing the syntax
the literals
for InlineArrays
:
// 'let' Variable ':' Type '=' Literal-value-for-Type
let u: [3 x Int] = [2, 3, 5]
let v: [3 x [3 x Int]] = [[0, 0, 1], [0, 1, 0], [1, 0, 0]]
// possible to flatten it?
let w: [3 x [3 x Int]] = [0, 0, 1, 0, 1, 0, 1, 0, 0]
This should work:
typealias Ints<let count: Int> = InlineArray<count, Int>
var x: Ints<3>
Please don't flatten it for nested inline arrays. C or C++ has that feature, and I don't like it because it makes the language irregular, and only exists because they don't support co-equal coordinates.
I did have flattened lists, but only for inline multiple extents:
let x: [2 of [3 of Int]] = [[1, 2, 3], [4, 5, 6]]
let y: [2 by 3 of Int] = [1, 2, 3, 4, 5, 6]
// Terms go in lexicographic order,
// where the rightmost coordinate cycles the fastest,
// down to the leftmost the slowest.
// (Regardless of storage order.)
Iām curious about your opinion on the bracket-less syntaxes that have been proposed here, such as n of T
and n x T
. I feel that these syntaxes imply the "inline-ness" of InlineArray
quite clearly, as well as being shorter and less cluttered than their bracketed counterparts:
var buffer: 1024 of UInt8
My feelings there are a bit less well-formed. On a purely ergonomic basis I bristle at those syntaxes due to the issues (I believe already mentioned up-thread) where e.g. Optional<any P>
must be written as (any P)?
instead of merely any P?
. And I also think it's helpful to understanding for the syntax to draw the immediate parallel with Array
since this think is indeed array-like in many important ways. So I'm a bit torn on what the best balance to strike is.
Yeah, when I see [1024 x UInt8]
, I imagine I can subscript it just like an Array
or Dictionary
. Moreover, after choosing InlineArray
over Vector
, I don't think it make sense to use a syntax that doesn't resemble Array
.
I think [5 x Int]
is fine, though not great. It conveys the idea of N
instances of Int
reasonably well.
The best alternative I can come up with is [5 repeat Int]
. It has appeal for re-using repeat
, but is a bit long and, well, a non-parameter-pack use of repeat
is likely a downside too.