[Pitch] InlineArray type sugar

Well, if this hopefully restarts movement to remove .self and allow bare meta types I'd be content but if that gets validly rejected I think it'd be a shame.


But, I'd be curious what some have to say about <5, Int>. While it can look gross when being used in conjunction with generics:

MemoryLayout<<5, Int>>

I personally only use Array/Dictionary sugar in variable/parameter declarations, not even for extensions or what I think is cursed:

[Int](repeating: 0, count: 5)
1 Like

Also from the future directions is this though:

Currently:

// This compiles, it's [Int](495), so would be ambiguous in the future.
let fiveInts = [5 * 99]
// This would be okay (doesn't compile yet).
let fiveByFive = [5 * [5 * 99]]
1 Like

I don't see why. You can't name a type 5 or 9, so it's not ambiguous. And if you use a symbol (integer generic or typename), the compiler knows which it is.

While this is...a direction, I think sugaring InlineArray<...> and sugaring its initializers are distinguishable. With just the type sugar, we'd be able to have something like [5 * _](repeating: 99), so it's not clear to me why the design space of the type sugar should be uncritically constrained a priori only to spellings that can also accommodate initializing values.

But...if we do want to do it all in one go, then [5 of 99] seems fine—indeed, even more appealing—to me; certainly less potentially problematic than making x an ersatz binary operator.


(In Swift, we have a bit of a history in eliding a similar problem, actually: We use + to mean actually adding numbers as well as concatenating strings. These are actually two quite disparate operations, yet we've mushed them together into one operator. Now, we're trying to find a multiplication-like notation for repeating elements which, if we're going to extend to values, users might want to reach for to repeat substrings also...)

7 Likes

I personally don't like the x or * separators, and the general idea of "Multiplication of the Type"; it just feels wrong to me. other than already mentioned cons with each of the separators. I discourage them.

on the other hand, I like Rust's [T; N] syntax, which I interpret as the following:
In English orthography,

The most common use of the semicolon is to join two independent clauses without using a conjunction such as "and".
-wikipedia (a cool wikipedia page all about semicolons :>)

so when I see [Int; 3] I think "an array of Integers (from [Int]); [also] an array of 3 elements".
which I think incapsulates the idea perfectly.
now since the generic arguments are swapped, that leaves us with [N; T] syntax.

now other than that syntax, I also like the [N of T] keyword approach. with reasons others mentioned like maybe later using it to enable some additional parameter pack features.

so tl;dr, I'm in favor of [3; Int] or [3 of Int]. and pretty much dislike the other approaches :>

13 Likes

I see what you mean, I might be thinking about this incorrectly, my argument was just specifically that let x = [5 * 9] already compiles and would collide with any other meaning.

3 Likes

Thanks, yeah I was assuming that the sugar would be exactly the same (just replacing * with x), but that need not be the case. I was also thinking that since * is already an operator and x isn't than by default x would be less ambiguous--but I see how this is sort of an incorrect deduction.

I do think of works pretty well since it's not something you'd typically name a variable, though I could see someone having an of(_:) method.

I think that's an argument against having [5 * 9] be syntactic sugar for "5 elements of inferred type Int, each equal to 99". As is the fact that x (or * or ; or however we end up spelling it) would not be transitive in this position, which is going to be confusing as heck to developers who aren't familiar with the (nonexistent, brand new) syntax. x is clearly meant to evoke multiplication even if it's not technically performing any arithmetic multiplication, but [5 x 9] ≠ [9 x 5].

I could buy of here as a keyword rather than an operator. I don't think it would make for good syntactic sugar as an expression though (let x = [5 of 10] I guess?), just as a type (let x: [5 of Int] = ...).

4 Likes

I think x is the most aesthetically pleasant, but to formalize a little bit on what’s already floating around, it seems that we need to pick a separator that’s made with operator characters to avoid ambiguities with identifiers.

1 Like

I think that's an argument against having [5 * 9] be syntactic sugar for "5 elements of inferred type Int , each equal to 99".

Yes completely I agree, thanks for helping me see that. After this discussion and thinking about it more, I'd lean more towards * than of with x being last/not preferred.

1 Like

I find the x to be a little confusing.

Since I could see a world were we may want to permit compile time operations on these values (such as methods were the return value is compile time computed function of on or more of the input values).

eg:

/**
interpolate values in an inline array adding a value between each existing value.
*/
func interpolate<let size: Int>(_ values:  [size x Int]) -> [(size * 2) - 1 x Double]

If this becomes a thing then having [5 x Int] is rather confusing.

I think ; as a separation of dimensions is a good indicator.

func interpolate<let size: Int>(_ values:  [size; Int]) -> [(size * 2) - 1; Double]

Or maybe separate the size from the value [5]<Int>. Could then also support an unpacking of multi nested [5]<[6]<Int>> to also be written and [5, 6]<Int>

func interpolate<let size: Int>(_ values:  [size]<Int>) -> [(size * 2) - 1]<Double>

For inferred types I would suggest keeping the <> just without a value in place. Eg.

let values: []<> = [1, 2, 3] but also maybe the need to have an inferred type shorthand is not needed for this type at all.

2 Likes

Do folks really anticipate using this type so frequently that it even warrants a custom sugar syntax? I personally think I will be a large adopter of InlineArray and am not looking forward to more bespoke syntax to teach others and handle in my macros.

IMO this sugar is being proposed/pitched too early and we should see if theres a real need for it.

47 Likes

Why is it a goal to evoke association with multiplication?
This isn't really that anyways.

To me, [5 x Int] looks much more unpleasant than the [5 Int] you find so unpleasant.
To me, it's an "ick", but I guess that's a personal opinion? Maybe because I come from a mathematics background.

¯\_(ツ)_/¯


Personally, I think this should be spelled with a non-letter ascii-symbol, and I think Rust's ; is up to the task, but ,, , * would also work well, in no particular order.

10 Likes

Using x is too magic for me. While * is more likely to create ambiguity, why don't we explore possibilities using other single-character token?

[5 ~ Int]
[5 ^ Int]

If one day we support value sugar, it will be more natural because that token could just be a special operator, let someInlineArray = [10 ~ a]

While the N x Type syntax is cute, I don't really see how it could be workable in practice. The [5 x x x Int] example seems rather fatal. I'm not a huge fan of ofeither, because it breaks down for multidimensional arrays. [7 of [9 of Borg]] is a bit too HyperTalk for me. , as already mentioned, is ambiguous with an array literal containing both numbers and metatypes, so I think that just leaves * and ;, of which I prefer ;

1 Like

Not sure how I feel about it, but for sake of disposition, should be consider adding stuff outside of the brakets? How does e.g. 5[Int] work and scale?

1 Like

Just a thought, but as an algebraic type this is an exponential, so the following could work:

[Int^5]
[[Int^5]^3]

Or:

[Int**5]
[[Int**5]**3]

However, this reverses the order and I can imagine that is not preferred. We do have an existing notation for exponentials that does use the proposed order, which is the arrow:

[5 -> Int]
[3 -> [5 -> Int]]

This may be too theoretical, though, and perhaps confusing.

2 Likes

I am totally in favour of using x in the sugar because it is so aesthetically pleasing.

Contrast this:

// [n x T]
[3 x Int]
[3 x [3 x Int]]
[3 x [3 x [3 x Int]]]

with this:

// [n of T]
[3 * Int]
[3 * [3 * Int]]
[3 * [3 * [3 * Int]]]

and with this:

// [n of T]
[3 of Int]
[3 of [3 of Int]]
[3 of [3 of [3 of Int]]]

and with this:

// [n; T]
[3; Int]
[3; [3; Int]]
[3; [3; [3; Int]]]

and finally with this:

// [T; n]
[Int; 3]
[[Int; 3]; 3]
[[[Int; 3]; 3]; 3]

It would be very sad, if anything other than x were accepted.

If I may copy @sliemeobn, I'll see myself out, Auf Wiedersehen. :slight_smile:

2 Likes

Yeah, I suggest removing this from “future directions” because it seems absurdsomewhat unrelated to the proposal and possibly controversial.

5 Likes

If it's a "5-Int Array", why is it not [5-Int]?

The English doesn't extend to higher dimensions directly but it does if you type alias, so it the concept backforms fine:

typealias Iamb = [2-String]
typealias Line = [5-Iamb]
typealias Sonnet = [14-Line] // AKA [14-5-2-String] or [14-[5-[2-String]]]