[Pitch] New Version of Array Proposal

It’s at <https://gist.github.com/CTMacUser/cfffa526b971d0e1f3a079f53c6819bb>.

* Try to clarify that fixed-size arrays are a new kind of compound type, not a (ridiculously magical) library generic type.
* Change the separator between the bounds and element type from a colon to a semicolon.
* Specify that violating the bounds of an array during indexing is a run-time error.
* Reword how the mapping of static-indexing elements for multi-dimensional arrays works.
* Completely redo array values/literals. A literal for a fixed-size array always includes a semicolon, which separates the bounds from the values. (The separator in FSA types was changed to a semicolon to match.) A value can be a plain expression or a dictionary expression where the right side of the colon is the value and the left side is the index of the target element or “default” for all un-targeted elements. The left side can also be “func”, with the right side being an initialization closure.
* Move the “Reshaping Arrays” section and add an example.
* Right now, deterministic initialization is unchanged, so an initializing loop has to be changed to initializing the array with a function term, moving the loop to the closure.
* Remove the “parallel” declaration attribute. Add a future note about it and the “fragmentary” attribute.
* Change the for-loop example to conform to deterministic initialization. Reword how the flattening buffer functions work.
* Add examples to element-wise casting.
* Reword tuple conversion section, and add an example.
* Reword various vector-mode attribute sections. Note that there need to be two ABI additions for FSA, one for non-vectorized FSAs and one for vectorized FSAs. These two kinds of arrays need conversion functions at our (i.e. the ABI) level, but are transparent for the user.

    let v1: @vector [3; Int] = [; 1, 3, 5]
    let v2: [3; Int] = v1 // Not a type-mismatch error

* Due to FSA’s literals including the bounds, or using automatic bounds mode, array-placeholder syntax is not longer needed for type annotations and has been removed.
* Renamed “nestings” to “layers”.

···


Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com

Hi, I’ve been watching this proposal for a while but didn’t get to read it
in detail until now. I really like this idea and it’s very comprehensive
and well-thought out. Some feedback:

1. I got very confused reading through the document the first time through.
You’re introducing a huge amount of new syntax and jargon, and not defining
all of them before first using it. For example, what does `a[[;3]]` and `b[[;1,
4]]` after the seventh paragraph mean? This bounds-omission syntax isn’t
explained until the Array Values section, and it’s buried in the middle of
a paragraph.

2. Using the semicolon as the bounds separator feels a bit weird,
especially when combined with the colons in the initializers. In English,
semicolons bind closer than colons do, so `[13, 2; [;0, 0]: 0, [;0, 1]: 0,
default: -1]` reads strangely. Also, where does the type annotation go?
Having a semicolon after a left square bracket “[;” also makes for ugly code
texture <http://elm-lang.org/blog/compilers-as-assistants>, something to
think about considering I’m going to be staring at *a lot* of these things.
Then again, I’m struggling to think up an alternative syntax.

3. Some of the syntax rules could benefit from providing a little
explanation, as the rationale is not always immediately obvious, for
example:

- Allowing a single number for the left side of the dictionary literal
pair, but not a comma-separated index list.

[5, 2; 2, 1: 5]

of course, the comma character is already reserved for separating
key-element pairs, but I had to think for a while before realizing that.

- `func` not being allowed to coexist with other initialization pairs. My
first thought was “why can’t `func:`initialize all the elements that
weren’t covered by anything else, like `default:` does? I guess this is to
ensure an efficient implementation of the closure initializer, but again,
this is non-obvious.

4. Please define terms like “term”, “array-index term” and “single-number
term” and “index set”, among others, preferably in boldface at the top of
the document. This proposal is hard enough to follow without having to
puzzle out what an “array-index term” is. (Isn’t *every* index an array
index term?)

5. There’s a lot of awkward and confusing language that I had to go back
and reread many times over to figure out what it meant. Examples: “*The
number to the left of the colon is the index of the element to receive the
value on the right of the colon.*” (what???), “*with an explicit bound
(set)*” (first thought: Why is “set” in parentheses? Does it not matter
whether you set an explicit bound?). Again, this proposal is hard enough to
follow without having to puzzle out that “(set)” refers to a list of
multiple indices, not *setting* a single index.

6. “*The element type of the array is determined by either the return type
of a function term's closure, or by the type consolidated from all the term
values (like in dictionary and standard-array literals).*” That second part
is going to kill the type checker, since I’m guessing in practice FSAs are
going to have much longer literals than flexible arrays or dictionaries do
right now.

7. You should add a section in the FAQ justifying the use of `as` for FSA
shape conversion. Why not initializer syntax?

let d = [4, 3; 1, 2, 3, default: 0]let e = [4, 3; Double](d)

I agree that `as` is better than magical initializers, but there are
legitimate arguments against `as` too. Right now, `as` is reserved for
literals and class types. Applying an `as` cast to a value type is an
entirely new use-context for the keyword.

8. What is a “sub-object”??? This word started randomly appearing halfway
through the document with no definition.

9. I don’t see the value in having both nested FSAs and multi-dimensional
FSAs. Aren’t they the same thing? For example, in the code snippet

let a = [;1, 2, 3, 4]
assert(a[0] == 1)
assert(a[1] == 2)
assert(a[2] == 3)
assert(a[3] == 4)
let b = a as [2, 2; Int]
assert(b[0, 0] == 1)
assert(b[0, 1] == 2)
assert(b[1, 0] == 3)
assert(b[1, 1] == 4)
let c = a as [2; [2; Int]]
assert(c[0][0] == 1)
assert(c[0][1] == 2)
assert(c[1][0] == 3)
assert(c[1][1] == 4)

There’s three syntaxes which accomplish two unique things. I lean towards
disallowing FSA nesting and instead allowing *incomplete* index lists to
partially unnest multidimensional FSAs. Let’s reserve “[][][]...” for
flexible array chained dereferencing.

10. I don’t see much value in runtime DI. It seems to add a lot of
complexity to the compiler with little benefit.

11. This should have defined behavior:

let data = [2, 2; 1, 2, 4, 8]
for (i, x) in data.enumerated()
{
    total += x
}

“with” methods should be used sparingly and not be part of common idioms
like iterating through an array as a buffer.

12. Can we rename the `cardinality` type property to `nestingCount` or
`nestingLevels` something similar? That seems a lot clearer and more
descriptive of that the property actually represents. I’d also bet that
close to no one knows what cardinality is.

···

On Sat, Jul 22, 2017 at 3:41 PM, Daryle Walker via swift-evolution < swift-evolution@swift.org> wrote:

It’s at <https://gist.github.com/CTMacUser/cfffa526b971d0e1f3a079f53c6819
>.

* Try to clarify that fixed-size arrays are a new kind of compound type,
*not* a (ridiculously magical) library generic type.
* Change the separator between the bounds and element type from a colon to
a semicolon.
* Specify that violating the bounds of an array during indexing is a
run-time error.
* Reword how the mapping of static-indexing elements for multi-dimensional
arrays works.
* Completely redo array values/literals. A literal for a fixed-size array
always includes a semicolon, which separates the bounds from the values.
(The separator in FSA types was changed to a semicolon to match.) A value
can be a plain expression or a dictionary expression where the right side
of the colon is the value and the left side is the index of the target
element or “default” for all un-targeted elements. The left side can also
be “func”, with the right side being an initialization closure.
* Move the “Reshaping Arrays” section and add an example.
* Right now, deterministic initialization is unchanged, so an initializing
loop has to be changed to initializing the array with a function term,
moving the loop to the closure.
* Remove the “parallel” declaration attribute. Add a future note about it
and the “fragmentary” attribute.
* Change the for-loop example to conform to deterministic initialization.
Reword how the flattening buffer functions work.
* Add examples to element-wise casting.
* Reword tuple conversion section, and add an example.
* Reword various vector-mode attribute sections. Note that there need to
be two ABI additions for FSA, one for non-vectorized FSAs and one for
vectorized FSAs. These two kinds of arrays need conversion functions at our
(i.e. the ABI) level, but are transparent for the user.

    let v1: @vector [3; Int] = [; 1, 3, 5]
    let v2: [3; Int] = v1 // Not a type-mismatch error

* Due to FSA’s literals including the bounds, or using automatic bounds
mode, array-placeholder syntax is not longer needed for type annotations
and has been removed.
* Renamed “nestings” to “layers”.


Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I wanted to read the proposal, but skipped it as soon as I’ve seen the syntax. From the esthetic point of you the proposed syntax is really ugly. Again I’m not speaking against the feature in general, nor against any of the technical benefits fixed-size array will provide to us. I simply dislike the syntax, which in my opinion does not fit to Swift.

I think it might be better not pushing the fixed-size arrays here at all, but instead envision everything from a larger perspective. Wouldn’t be great to introduce value subtyping. Then enhance subtyping in general to allow static behavioral subtyping (also called type refinement)?

I’d love to see something like that happen to Swift. Arrays could have behavior subtypes which would allow fixed-size arrays. Numerical types could be bounded with ranges and provide a lot of compile time benefits.

···

Am 22. Juli 2017 um 21:42:02, Daryle Walker via swift-evolution (swift-evolution@swift.org) schrieb:

It’s at <https://gist.github.com/CTMacUser/cfffa526b971d0e1f3a079f53c6819bb>.

* Try to clarify that fixed-size arrays are a new kind of compound type, not a (ridiculously magical) library generic type.
* Change the separator between the bounds and element type from a colon to a semicolon.
* Specify that violating the bounds of an array during indexing is a run-time error.
* Reword how the mapping of static-indexing elements for multi-dimensional arrays works.
* Completely redo array values/literals. A literal for a fixed-size array always includes a semicolon, which separates the bounds from the values. (The separator in FSA types was changed to a semicolon to match.) A value can be a plain expression or a dictionary expression where the right side of the colon is the value and the left side is the index of the target element or “default” for all un-targeted elements. The left side can also be “func”, with the right side being an initialization closure.
* Move the “Reshaping Arrays” section and add an example.
* Right now, deterministic initialization is unchanged, so an initializing loop has to be changed to initializing the array with a function term, moving the loop to the closure.
* Remove the “parallel” declaration attribute. Add a future note about it and the “fragmentary” attribute.
* Change the for-loop example to conform to deterministic initialization. Reword how the flattening buffer functions work.
* Add examples to element-wise casting.
* Reword tuple conversion section, and add an example.
* Reword various vector-mode attribute sections. Note that there need to be two ABI additions for FSA, one for non-vectorized FSAs and one for vectorized FSAs. These two kinds of arrays need conversion functions at our (i.e. the ABI) level, but are transparent for the user.

let v1: @vector \[3; Int\] = \[; 1, 3, 5\]
let v2: \[3; Int\] = v1  // Not a type\-mismatch error

* Due to FSA’s literals including the bounds, or using automatic bounds mode, array-placeholder syntax is not longer needed for type annotations and has been removed.
* Renamed “nestings” to “layers”.


Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Hi, I’ve been watching this proposal for a while but didn’t get to read it in detail until now. I really like this idea and it’s very comprehensive and well-thought out. Some feedback:

1. I got very confused reading through the document the first time through. You’re introducing a huge amount of new syntax and jargon, and not defining all of them before first using it. For example, what does `a[[;3]]` and `b[[;1, 4]]` after the seventh paragraph mean? This bounds-omission syntax isn’t explained until the Array Values section, and it’s buried in the middle of a paragraph.

I had to rearrange other sections of the document due to this problem. I saw this, but couldn’t figure out how to resolve it at that time; the sections seemed too mutually dependent (so I get this problem either way).

2. Using the semicolon as the bounds separator feels a bit weird, especially when combined with the colons in the initializers. In English, semicolons bind closer than colons do, so `[13, 2; [;0, 0]: 0, [;0, 1]: 0, default: -1]` reads strangely. Also, where does the type annotation go? Having a semicolon after a left square bracket “[;” also makes for ugly code texture <http://elm-lang.org/blog/compilers-as-assistants>, something to think about considering I’m going to be staring at a lot of these things. Then again, I’m struggling to think up an alternative syntax.

I would prefer to drop the semicolons to describe the arrays used to index locations, but I don’t know if using standard array literals would make parsing harder for implementors. And we could need different confirmation code that the indexes are valid for the shape of the array.

Within a FSA literal, the element type is calculated from the expression type, like in standard array and dictionary literals. That’s real easy if the literal uses a function term (It’s the closure’s return type.), but having mismatch element types in other kinds of terms is an error (I think) like it is for the other composite literals.

For your specific example, you can use storage order: `[13, 2; 0, 0, default: -1]`.

3. Some of the syntax rules could benefit from providing a little explanation, as the rationale is not always immediately obvious, for example:

- Allowing a single number for the left side of the dictionary literal pair, but not a comma-separated index list.

[5, 2; 2, 1: 5]

of course, the comma character is already reserved for separating key-element pairs, but I had to think for a while before realizing that.

- `func` not being allowed to coexist with other initialization pairs. My first thought was “why can’t `func:`initialize all the elements that weren’t covered by anything else, like `default:` does? I guess this is to ensure an efficient implementation of the closure initializer, but again, this is non-obvious.

Yeah, allowing a function term to share with others would either mean elements get double initialized (or one takes priority, and what defines the order/priority) or the implementation logic to loop over every element needs extra tests to skip elements with their own initialization term.

4. Please define terms like “term”, “array-index term” and “single-number term” and “index set”, among others, preferably in boldface at the top of the document. This proposal is hard enough to follow without having to puzzle out what an “array-index term” is. (Isn’t every index an array index term?)

5. There’s a lot of awkward and confusing language that I had to go back and reread many times over to figure out what it meant. Examples: “The number to the left of the colon is the index of the element to receive the value on the right of the colon.” (what???), “with an explicit bound (set)” (first thought: Why is “set” in parentheses? Does it not matter whether you set an explicit bound?). Again, this proposal is hard enough to follow without having to puzzle out that “(set)” refers to a list of multiple indices, not setting a single index.

6. “The element type of the array is determined by either the return type of a function term's closure, or by the type consolidated from all the term values (like in dictionary and standard-array literals).” That second part is going to kill the type checker, since I’m guessing in practice FSAs are going to have much longer literals than flexible arrays or dictionaries do right now.

Hopefully, most literals will use default or function terms.

7. You should add a section in the FAQ justifying the use of `as` for FSA shape conversion. Why not initializer syntax?
let d = [4, 3; 1, 2, 3, default: 0]
let e = [4, 3; Double](d)
I agree that `as` is better than magical initializers, but there are legitimate arguments against `as` too. Right now, `as` is reserved for literals and class types. Applying an `as` cast to a value type is an entirely new use-context for the keyword.

I don’t think any compound type has initializers; I didn’t add them to FSAs to avoid the impression that custom initializers are possible.

Theoretically, some reshaping could be expressed as a library function:

let f = reshape<2, 3, 2>(e) // [2, 3, 2; Double]

but that requires complete generics

func reshape<N…: Int, M…: Int, T>(_ x: […M; T]) -> […N; T] where #product(N…) == #product(M…) {
return […N; func: { x[ reindex<…M>($0) ] }
}

and it doesn’t work when the element types are different (even if the inner non-array types are the same). Then “as” can be reserved for just element-wise casts (which your “e” is, not a reshape). Speaking of which, maybe introduce a new “as”:

let g = e as@ Float

where you give only the element type and the compiler figures out the shape. We would probably need “as@?” and “as@!” too. I mentioned that these could be library generic functions too someday.

8. What is a “sub-object”??? This word started randomly appearing halfway through the document with no definition.

That should be a computer-science standard term-of-art. Here, it’s a tuple member or FSA element. The other product types, classes and structures, have them too, but are hidden behind encapsulation.

9. I don’t see the value in having both nested FSAs and multi-dimensional FSAs. Aren’t they the same thing? For example, in the code snippet

Why does any language with multi-dimensional arrays (like Fortran or Ada) have them? By this standard, no language should have multi-dimensional arrays. They exist because of data modeling. Co-equal coordinates in the model should be co-equal in their representation in the program. Yes, they are implemented the same way underneath. We don’t copy C everywhere, so why not take this opportunity to do better. Also, just using nesting could imply that the intermediate array types have a meaning, but they might not if they’re just implementation quirks and not part of the abstract model.

Nested arrays are not my solution for multi-coordinate indexing; use multi-dimensional arrays for that. I mention nested arrays because:

Nested arrays fundamentally cannot be banned. (What if an element type is hidden behind a type-alias and is conditionally an array type?)
I need the definition to explain the “inner non-array type”
I need the inner non-array type to explain which pairings of FSAs for reshaping are legal. (And a similar reason for tuple conversion.) Note the two types can have different nesting levels.
I need to explain that empty arrays cannot be an array element type. (Should this be changed? What happens with tuples or structures containing empty tuples/structures as members? What about empty tuples/sturctures in “Array”? Banning empty arrays means we don’t have to worry about every array element being at the same address. The other way to solve this is to make them one byte (or word) long.)

let a = [;1, 2, 3, 4]
assert(a[0] == 1)
assert(a[1] == 2)
assert(a[2] == 3)
assert(a[3] == 4)
let b = a as [2, 2; Int]
assert(b[0, 0] == 1)
assert(b[0, 1] == 2)
assert(b[1, 0] == 3)
assert(b[1, 1] == 4)
let c = a as [2; [2; Int]]
assert(c[0][0] == 1)
assert(c[0][1] == 2)
assert(c[1][0] == 3)
assert(c[1][1] == 4)

There’s three syntaxes which accomplish two unique things. I lean towards disallowing FSA nesting and instead allowing incomplete index lists to partially unnest multidimensional FSAs. Let’s reserve “[][][]...” for flexible array chained dereferencing.

I don’t understand what your incomplete index list idea is. And as I said, the chaining technique is less I desire it and more I can’t ban it and keep Swift features orthogonal.

10. I don’t see much value in runtime DI. It seems to add a lot of complexity to the compiler with little benefit.

In other languages, people initialize their arrays in loops with run-time indexes. This frustrates compile-time DI. C doesn’t have this problem because it doesn’t have DI and is willing to risk undefined behavior during uninitialized reads. So it’s either work in the confines of static DI, allow dynamic DI (hopefully under as limited circumstances as possible), or give up on checking for FSAs and allow undefined behavior (which would blow a big hole in static DI). Maybe this can be pushed to version 2.

11. This should have defined behavior:

let data = [2, 2; 1, 2, 4, 8]
for (i, x) in data.enumerated()
{
    total += x
}

FSAs are compound types, not named types, so there are no methods. (Just like tuples, FSAs don’t follow any protocols.) But since they’re a built-in, I can customize the “for-in” loop to cover all the elements in an implementation-optimized order. (The implementation has the option of multi-threading if it can see there’s no cross-iteration contamination.) Since you can’t control the order without manual looping, the “#indexOf” expression exists to let you know where you are during an iteration loop.

I first used the iteration variable as the operand to “#indexOf” to determine which array instance to track. Then I saw that the for-loop implements its iteration variable as a pattern in the grammar instead of something simpler. And what happens if a wildcard is used as the iteration variable? And would people question why when the pattern has multiple iteration variables, all are equally valid for “#indexOf”? I moved the operand to be the label because we usually don’t have multiple labels on statements (Is that even legal in Swift?) and can optimize which loops need to be indexable.

“with” methods should be used sparingly and not be part of common idioms like iterating through an array as a buffer.

I need some library function to convert a FSA into a Collection, and to represent the “T[]” function parameter type concept from C, which represents any array size (instead of being locked for one length, or since we have multi-dimensional arrays, one shape). For safety, the callback function uses the buffer version so you have a pointer and size. You generally should be using the for-loop, though.

Using an unsafe buffer pointer forces the data into addressable memory, which isn’t necessarily the case of an instance’s location by default. (C optimizes the other way. Objects are addressable by default, and that is disabled, and any optimizations by being un-aliased activated, by the “restrict” qualifier.)

We should have some way to cull specific indexes in loops. (Like “lock index 2 to column 5 but iterate over all the qualifying subset of elements.”) I don’t know if this has to be a new base operation on a loop, or if it can be done through a library function, or if it can be done through a library function after we complete generics. But I think it can wait until version 2.

12. Can we rename the `cardinality` type property to `nestingCount` or `nestingLevels` something similar? That seems a lot clearer and more descriptive of that the property actually represents. I’d also bet that close to no one knows what cardinality is.

The nesting-level is “layers.” “Cardinality” is the number of co-equal coordinates. But that second name probably needs some work, although it can be synthesized from the length of “dimensions.” The “cardinality” and “elementCount” properties work around us not having (integer) compile-time constants yet; otherwise we can derive them from compile-time expressions on “dimensions."

···

On Jul 22, 2017, at 9:14 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:


Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com

On Sat, Jul 22, 2017 at 3:41 PM, Daryle Walker via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
It’s at <https://gist.github.com/CTMacUser/cfffa526b971d0e1f3a079f53c6819bb>.

* Try to clarify that fixed-size arrays are a new kind of compound type, not a (ridiculously magical) library generic type.
* Change the separator between the bounds and element type from a colon to a semicolon.
* Specify that violating the bounds of an array during indexing is a run-time error.
* Reword how the mapping of static-indexing elements for multi-dimensional arrays works.
* Completely redo array values/literals. A literal for a fixed-size array always includes a semicolon, which separates the bounds from the values. (The separator in FSA types was changed to a semicolon to match.) A value can be a plain expression or a dictionary expression where the right side of the colon is the value and the left side is the index of the target element or “default” for all un-targeted elements. The left side can also be “func”, with the right side being an initialization closure.
* Move the “Reshaping Arrays” section and add an example.
* Right now, deterministic initialization is unchanged, so an initializing loop has to be changed to initializing the array with a function term, moving the loop to the closure.
* Remove the “parallel” declaration attribute. Add a future note about it and the “fragmentary” attribute.
* Change the for-loop example to conform to deterministic initialization. Reword how the flattening buffer functions work.
* Add examples to element-wise casting.
* Reword tuple conversion section, and add an example.
* Reword various vector-mode attribute sections. Note that there need to be two ABI additions for FSA, one for non-vectorized FSAs and one for vectorized FSAs. These two kinds of arrays need conversion functions at our (i.e. the ABI) level, but are transparent for the user.

    let v1: @vector [3; Int] = [; 1, 3, 5]
    let v2: [3; Int] = v1 // Not a type-mismatch error

* Due to FSA’s literals including the bounds, or using automatic bounds mode, array-placeholder syntax is not longer needed for type annotations and has been removed.
* Renamed “nestings” to “layers”.


Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

I wanted to read the proposal, but skipped it as soon as I’ve seen the
syntax. From the esthetic point of you the proposed syntax is really ugly.
Again I’m not speaking against the feature in general, nor against any of
the technical benefits fixed-size array will provide to us. I simply
dislike the syntax, which in my opinion does not fit to Swift.

What about a double colon?

let fsa:[5, 2::Int] = [5, 2::[::0, 0]: 5, [::5, 1]: 6, default: -1]

I think it might be better not pushing the fixed-size arrays here at all,

but instead envision everything from a larger perspective. Wouldn’t be
great to introduce value subtyping. Then enhance subtyping in general to
allow static behavioral subtyping (also called type refinement)?

I’d love to see something like that happen to Swift. Arrays could have
behavior subtypes which would allow fixed-size arrays. Numerical types
could be bounded with ranges and provide a lot of compile time benefits.

This sounds like a recipe for never making fixed size arrays happen at all.
I’d rather actually get the base feature (contiguous variables) in first,
then worry about static behavioral subtyping later.

···

On Sun, Jul 23, 2017 at 5:29 AM, Adrian Zubarev via swift-evolution < swift-evolution@swift.org> wrote:

Am 22. Juli 2017 um 21:42:02, Daryle Walker via swift-evolution (
swift-evolution@swift.org) schrieb:

It’s at <https://gist.github.com/CTMacUser/cfffa526b971d0e1f3a079f53c6819
>.

* Try to clarify that fixed-size arrays are a new kind of compound type,
*not* a (ridiculously magical) library generic type.
* Change the separator between the bounds and element type from a colon to
a semicolon.
* Specify that violating the bounds of an array during indexing is a
run-time error.
* Reword how the mapping of static-indexing elements for multi-dimensional
arrays works.
* Completely redo array values/literals. A literal for a fixed-size array
always includes a semicolon, which separates the bounds from the values.
(The separator in FSA types was changed to a semicolon to match.) A value
can be a plain expression or a dictionary expression where the right side
of the colon is the value and the left side is the index of the target
element or “default” for all un-targeted elements. The left side can also
be “func”, with the right side being an initialization closure.
* Move the “Reshaping Arrays” section and add an example.
* Right now, deterministic initialization is unchanged, so an initializing
loop has to be changed to initializing the array with a function term,
moving the loop to the closure.
* Remove the “parallel” declaration attribute. Add a future note about it
and the “fragmentary” attribute.
* Change the for-loop example to conform to deterministic initialization.
Reword how the flattening buffer functions work.
* Add examples to element-wise casting.
* Reword tuple conversion section, and add an example.
* Reword various vector-mode attribute sections. Note that there need to
be two ABI additions for FSA, one for non-vectorized FSAs and one for
vectorized FSAs. These two kinds of arrays need conversion functions at our
(i.e. the ABI) level, but are transparent for the user.

    let v1: @vector [3; Int] = [; 1, 3, 5]
    let v2: [3; Int] = v1 // Not a type-mismatch error

* Due to FSA’s literals including the bounds, or using automatic bounds
mode, array-placeholder syntax is not longer needed for type annotations
and has been removed.
* Renamed “nestings” to “layers”.


Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I simply dislike the syntax, which in my opinion does not fit to Swift.

+1

Also, I might have a different focus for the feature:
Performance and C interoperability are important, but I just want type safety and to avoid creating stupid things like Vector3, Vector4… which can't share code because there's no inheritance for structs (yet), and which are limited in expressiveness.

I don't think arrays should be multidimensional: Memory has only one dimension, this is a low level feature — and it's easy to build multi-dimensional structures on top of simple arrays.

I also have little need for a special syntax for literals: When the array is used as communication medium ("this function returns an array of size 3"), I'm not using literals at all, and when I'm declaring a array for my own use, I wouldn't mind if the compiler decides on his own that it can be fixed size.
There's also the use case of creating an array that will be handed over to a method that expects a FSA, but I wouldn't mind if I have to declare the type explicitly in this case.

Bottom line:
I prefer only one addition, and that is an extension of the generics system that has already been brought up in the manifesto.
With generics, the syntax for arrays is quite intuitive ("let speed: Array<Float, size: 3>"), and the usefulness isn't limited to FSAs alone — after all, Swift is used in production for several years now without proper support for them.

Tino

I wanted to read the proposal, but skipped it as soon as I’ve seen the syntax. From the esthetic point of you the proposed syntax is really ugly. Again I’m not speaking against the feature in general, nor against any of the technical benefits fixed-size array will provide to us. I simply dislike the syntax, which in my opinion does not fit to Swift.

What about a double colon?

let fsa:[5, 2::Int] = [5, 2::[::0, 0]: 5, [::5, 1]: 6, default: -1]

I thought we'd mostly settled on "let fsa: [count * Type]" last time this came up.

- Dave Sweeris

···

Sent from my iPhone

On Jul 23, 2017, at 08:45, Taylor Swift via swift-evolution <swift-evolution@swift.org> wrote:

On Sun, Jul 23, 2017 at 5:29 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

9. I don’t see the value in having both nested FSAs and multi-dimensional
FSAs. Aren’t they the same thing? For example, in the code snippet

Why does any language with multi-dimensional arrays (like Fortran or Ada)
have them? By this standard, no language should have multi-dimensional
arrays. They exist because of data modeling. Co-equal coordinates in the
model should be co-equal in their representation in the program. Yes, they
are implemented the same way underneath. We don’t copy C everywhere, so why
not take this opportunity to do better. Also, just using nesting could
imply that the intermediate array types have a meaning, but they might not
if they’re just implementation quirks and not part of the abstract model.

Nested arrays are not my solution for multi-coordinate indexing; use
multi-dimensional arrays for that. I mention nested arrays because:

   - Nested arrays fundamentally cannot be banned. (What if an element
   type is hidden behind a type-alias and is conditionally an array type?)

Doesn’t Swift have to resolve the types at some point anyway? If it’s

impossible to ban, we can allow it, but still make it unidiomatic. Nested
arrays are much messier to write than multidimensional arrays.

   - I need the definition to explain the “inner non-array type”
      - I need the inner non-array type to explain which pairings of FSAs
      for reshaping are legal. (And a similar reason for tuple conversion.) Note
      the two types can have different nesting levels.
   - I need to explain that empty arrays cannot be an array element type.
   (Should this be changed? What happens with tuples or structures containing
   empty tuples/structures as members? What about empty tuples/sturctures in
   “Array”? Banning empty arrays means we don’t have to worry about every
   array element being at the same address. The other way to solve this is to
   make them one byte (or word) long.)

let a = [;1, 2, 3, 4]
assert(a[0] == 1)
assert(a[1] == 2)
assert(a[2] == 3)
assert(a[3] == 4)
let b = a as [2, 2; Int]
assert(b[0, 0] == 1)
assert(b[0, 1] == 2)
assert(b[1, 0] == 3)
assert(b[1, 1] == 4)
let c = a as [2; [2; Int]]
assert(c[0][0] == 1)
assert(c[0][1] == 2)
assert(c[1][0] == 3)
assert(c[1][1] == 4)

There’s three syntaxes which accomplish two unique things. I lean towards
disallowing FSA nesting and instead allowing *incomplete* index lists to
partially unnest multidimensional FSAs. Let’s reserve “[][][]...” for
flexible array chained dereferencing.

I don’t understand what your incomplete index list idea is. And as I said,
the chaining technique is less I desire it and more I can’t ban it and keep
Swift features orthogonal.

Incomplete indexing means

let fsa:[5, 2; Int] = [5, 2; 2, 4, 3, 5, 4, 6, 5, 7, 6, 8]
fsa[3] // [2; 5, 7]

this would be the same as writing

let fsa:[5; [2; Int]] = [5; [2; 2, 4], [2; 3, 5], [2; 4, 6], [2; 5, 7], [2; 6,
8]]
fsa[3] // [2; 5, 7]

in your current system. This would obviate the need to nest FSAs for the
purpose of extracting entire rows of data.

10. I don’t see much value in runtime DI. It seems to add a lot of
complexity to the compiler with little benefit.

In other languages, people initialize their arrays in loops with run-time
indexes. This frustrates compile-time DI. C doesn’t have this problem
because it doesn’t have DI and is willing to risk undefined behavior during
uninitialized reads. So it’s either work in the confines of static DI,
allow dynamic DI (hopefully under as limited circumstances as possible), or
give up on checking for FSAs and allow undefined behavior (which would blow
a big hole in static DI). Maybe this can be pushed to version 2.

11. This should have defined behavior:

let data = [2, 2; 1, 2, 4, 8]
for (i, x) in data.enumerated()
{
    total += x
}

FSAs are compound types, not named types, so there are no methods. (Just
like tuples, FSAs don’t follow any protocols.) But since they’re a
built-in, I can customize the “for-in” loop to cover all the elements in an
implementation-optimized order. (The implementation has the option of
multi-threading if it can see there’s no cross-iteration contamination.)
Since you can’t control the order without manual looping, the “#indexOf
expression exists to let you know where you are during an iteration loop.

I first used the iteration variable as the operand to “#indexOf” to
determine which array instance to track. Then I saw that the for-loop
implements its iteration variable as a pattern in the grammar instead of
something simpler. And what happens if a wildcard is used as the iteration
variable? And would people question why when the pattern has multiple
iteration variables, all are equally valid for “#indexOf”? I moved the
operand to be the label because we usually don’t have multiple labels on
statements (Is that even legal in Swift?) and can optimize which loops need
to be indexable.

I really dislike the addition of magical builtin variables. Iterating
through a FSA should just take the FSA down one dimension level, so

let data = [2, 3; 1, 2, 3, 4, 8, 12]
for x:[3;] in data
{
    total += x[0] + x[1] + x[2]
}

would be the pattern.

“with” methods should be used sparingly and not be part of common idioms
like iterating through an array as a buffer.

I need some library function to convert a FSA into a Collection, and to
represent the “T[]” function parameter type concept from C, which
represents any array size (instead of being locked for one length, or since
we have multi-dimensional arrays, one shape). For safety, the callback
function uses the buffer version so you have a pointer and size. You
generally should be using the for-loop, though.

Using an unsafe buffer pointer forces the data into addressable memory,
which isn’t necessarily the case of an instance’s location by default. (C
optimizes the other way. Objects are addressable by default, and that is
disabled, and any optimizations by being un-aliased activated, by the
“restrict” qualifier.)

We should have some way to cull specific indexes in loops. (Like “lock
index 2 to column 5 but iterate over all the qualifying subset of
elements.”) I don’t know if this has to be a new base operation on a loop,
or if it can be done through a library function, or if it can be done
through a library function after we complete generics. But I think it can
wait until version 2.

12. Can we rename the `cardinality` type property to `nestingCount` or
`nestingLevels` something similar? That seems a lot clearer and more
descriptive of that the property actually represents. I’d also bet that
close to no one knows what cardinality is.

The nesting-level is “layers.” “Cardinality” is the number of co-equal
coordinates. But that second name probably needs some work, although it can
be synthesized from the length of “dimensions.” The “cardinality” and
“elementCount” properties work around us not having (integer) compile-time
constants yet; otherwise we can derive them from compile-time expressions
on “dimensions."

I understand there is currently a distinction due to FSA nesting, however I
don’t see why FSA nesting has to be a first-class citizen in this model.
Multidimensionality should be the idiomatic way to add nesting to an FSA.

···

On Sun, Jul 23, 2017 at 3:08 AM, Daryle Walker <darylew@mac.com> wrote:


Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com

On Sat, Jul 22, 2017 at 3:41 PM, Daryle Walker via swift-evolution < > swift-evolution@swift.org> wrote:

It’s at <https://gist.github.com/CTMacUser/cfffa526b971d0e1f3a079f53
c6819bb>.

* Try to clarify that fixed-size arrays are a new kind of compound type,
*not* a (ridiculously magical) library generic type.
* Change the separator between the bounds and element type from a colon
to a semicolon.
* Specify that violating the bounds of an array during indexing is a
run-time error.
* Reword how the mapping of static-indexing elements for
multi-dimensional arrays works.
* Completely redo array values/literals. A literal for a fixed-size array
always includes a semicolon, which separates the bounds from the values.
(The separator in FSA types was changed to a semicolon to match.) A value
can be a plain expression or a dictionary expression where the right side
of the colon is the value and the left side is the index of the target
element or “default” for all un-targeted elements. The left side can also
be “func”, with the right side being an initialization closure.
* Move the “Reshaping Arrays” section and add an example.
* Right now, deterministic initialization is unchanged, so an
initializing loop has to be changed to initializing the array with a
function term, moving the loop to the closure.
* Remove the “parallel” declaration attribute. Add a future note about it
and the “fragmentary” attribute.
* Change the for-loop example to conform to deterministic initialization.
Reword how the flattening buffer functions work.
* Add examples to element-wise casting.
* Reword tuple conversion section, and add an example.
* Reword various vector-mode attribute sections. Note that there need to
be two ABI additions for FSA, one for non-vectorized FSAs and one for
vectorized FSAs. These two kinds of arrays need conversion functions at our
(i.e. the ABI) level, but are transparent for the user.

    let v1: @vector [3; Int] = [; 1, 3, 5]
    let v2: [3; Int] = v1 // Not a type-mismatch error

* Due to FSA’s literals including the bounds, or using automatic bounds
mode, array-placeholder syntax is not longer needed for type annotations
and has been removed.
* Renamed “nestings” to “layers”.


Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Using the *multiplication operator* as a *separator* character seems like
an extraordinarily bad idea.

let fsa:[2 * Int] = [2 * 5, 3] // [10, 3] ???

···

On Sun, Jul 23, 2017 at 11:59 AM, David Sweeris <davesweeris@mac.com> wrote:

Sent from my iPhone

On Jul 23, 2017, at 08:45, Taylor Swift via swift-evolution < > swift-evolution@swift.org> wrote:

On Sun, Jul 23, 2017 at 5:29 AM, Adrian Zubarev via swift-evolution < > swift-evolution@swift.org> wrote:

I wanted to read the proposal, but skipped it as soon as I’ve seen the
syntax. From the esthetic point of you the proposed syntax is really ugly.
Again I’m not speaking against the feature in general, nor against any of
the technical benefits fixed-size array will provide to us. I simply
dislike the syntax, which in my opinion does not fit to Swift.

What about a double colon?

let fsa:[5, 2::Int] = [5, 2::[::0, 0]: 5, [::5, 1]: 6, default: -1]

I thought we'd mostly settled on "let fsa: [count * Type]" last time this
came up.

- Dave Sweeris

Well even if the behavioral subtyping might not fit for hundred percent in this scenario, my main point is that I don’t want Swift to contain exclusive features.

By that I mean that it’s far better to come up with something that has more potential and where the syntax could be reused for other *similar* purposes later on. Otherwise we’ll simply burn something now or create something that will not fit the future version of the language.

For instances limited numeric types *might* look like this Double<0 … 1.0>, UInt8<0 … 4>.

But this is only bike shedding and it has to be fleshed out in the future.

On the other hand if you think about the big picture here, we’d might come up with a syntax for fixed-sized arrays which we’ll reuse later for instance for behavioral conditions.

// again this is only some bikeshedding
let fixed: [Int]<4> = [1, 2, 3, 4] // `<4>` at the end is omitted

let fixed = [1, 2, 3, 4]<4>
Type<…> is ambiguous
Type[…] should be reserved for type subscripts

[…]Type might be reserved for property behaviors
<…>Type might look odd with generic types, but could work
Type(…) ambiguous but yet interesting in type context

let arr1: Array<Int>(size: 3) = [1, 2, 3]
let arr2: [Int](size: 3) = [1, 2, 3]
let arr3 = Array<Int>(size: 3)() ambiguous

I'd tend to some kind of type prefix syntax.

let arr4: <3>[Int] = [1, 2, 3]
let num1: <1.0 … 5.0>Double = …

let arr5: <2>@[Int] = [1, 2]
let num2: <0 … 1>@Double = …
Obviously none of them are visually pleasant to my eyes.

Am 23. Juli 2017 um 17:45:57, Taylor Swift (kelvin13ma@gmail.com) schrieb:

···

On Sun, Jul 23, 2017 at 5:29 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:
I wanted to read the proposal, but skipped it as soon as I’ve seen the syntax. From the esthetic point of you the proposed syntax is really ugly. Again I’m not speaking against the feature in general, nor against any of the technical benefits fixed-size array will provide to us. I simply dislike the syntax, which in my opinion does not fit to Swift.

What about a double colon?

let fsa:[5, 2::Int] = [5, 2::[::0, 0]: 5, [::5, 1]: 6, default: -1]

I think it might be better not pushing the fixed-size arrays here at all, but instead envision everything from a larger perspective. Wouldn’t be great to introduce value subtyping. Then enhance subtyping in general to allow static behavioral subtyping (also called type refinement)?

I’d love to see something like that happen to Swift. Arrays could have behavior subtypes which would allow fixed-size arrays. Numerical types could be bounded with ranges and provide a lot of compile time benefits.

This sounds like a recipe for never making fixed size arrays happen at all. I’d rather actually get the base feature (contiguous variables) in first, then worry about static behavioral subtyping later.

Am 22. Juli 2017 um 21:42:02, Daryle Walker via swift-evolution (swift-evolution@swift.org) schrieb:

It’s at <https://gist.github.com/CTMacUser/cfffa526b971d0e1f3a079f53c6819bb>.

* Try to clarify that fixed-size arrays are a new kind of compound type, not a (ridiculously magical) library generic type.
* Change the separator between the bounds and element type from a colon to a semicolon.
* Specify that violating the bounds of an array during indexing is a run-time error.
* Reword how the mapping of static-indexing elements for multi-dimensional arrays works.
* Completely redo array values/literals. A literal for a fixed-size array always includes a semicolon, which separates the bounds from the values. (The separator in FSA types was changed to a semicolon to match.) A value can be a plain expression or a dictionary expression where the right side of the colon is the value and the left side is the index of the target element or “default” for all un-targeted elements. The left side can also be “func”, with the right side being an initialization closure.
* Move the “Reshaping Arrays” section and add an example.
* Right now, deterministic initialization is unchanged, so an initializing loop has to be changed to initializing the array with a function term, moving the loop to the closure.
* Remove the “parallel” declaration attribute. Add a future note about it and the “fragmentary” attribute.
* Change the for-loop example to conform to deterministic initialization. Reword how the flattening buffer functions work.
* Add examples to element-wise casting.
* Reword tuple conversion section, and add an example.
* Reword various vector-mode attribute sections. Note that there need to be two ABI additions for FSA, one for non-vectorized FSAs and one for vectorized FSAs. These two kinds of arrays need conversion functions at our (i.e. the ABI) level, but are transparent for the user.

let v1: @vector \[3; Int\] = \[; 1, 3, 5\]
let v2: \[3; Int\] = v1  // Not a type\-mismatch error

* Due to FSA’s literals including the bounds, or using automatic bounds mode, array-placeholder syntax is not longer needed for type annotations and has been removed.
* Renamed “nestings” to “layers”.


Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Also, I might have a different focus for the feature:
Performance and C interoperability are important, but I just want type safety and to avoid creating stupid things like Vector3, Vector4… which can't share code because there's no inheritance for structs (yet), and which are limited in expressiveness.

I don't think arrays should be multidimensional: Memory has only one dimension, this is a low level feature — and it's easy to build multi-dimensional structures on top of simple arrays.

K&R in their original C decided to make arrays a low-level type, are removed the mid-level features other languages had for their arrays. I’m deciding in the opposite direction, allowing FSAs to be mid-level types, and adding (back) features appropriately.

Also, a core goal of FSAs is a user gateway to processor vector-unit types, and I didn’t want to ban 2D vector unit types (if they exist).

I also have little need for a special syntax for literals: When the array is used as communication medium ("this function returns an array of size 3"), I'm not using literals at all, and when I'm declaring a array for my own use, I wouldn't mind if the compiler decides on his own that it can be fixed size.
There's also the use case of creating an array that will be handed over to a method that expects a FSA, but I wouldn't mind if I have to declare the type explicitly in this case.

I originally used standard array literals for FSAs. And other people complained about that!

The problem is that the only way to know if an array literal is supposed to go to a FSA instead of an instantiation of Array is the surrounding context. And some people didn’t like that; they wanted the type to be (almost) always determined by the literal alone. In the old way, I needed a “let x: [_: Int] = [1, 2, 3, 4]” wildcard construct to allow automatic determination of the length. By including the array shape, we can differentiate FSAs from Array.

···

On Jul 24, 2017, at 7:29 AM, Tino Heth <2th@gmx.de> wrote:


Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com

let fsa:[2 * Int] = [2 * 5, 3] // [10, 3] ???

Correct. If you wanted a multidimensional array, that'd be written "let nestedFSA: [2*[5*Int]]". Or, speculating a bit, I suppose maybe "let nestedFSA: [[5*Int]*2]", if we wanted there to be a column-major option. IMHO all those read better than this proposal's syntax.

Using the multiplication operator as a separator character seems like an extraordinarily bad idea.

Well, much to the dismay of my occasionally-inner Unicode fanboy, we decided that we didn't want to make everyone have to figure out how to type "×" to use a stdlib type (to the further dismay of my O-IUF, this is actually a really good argument, especially since a likely use for FSA is in embedded or systems programming, where you might need to use an editor that doesn't support Unicode).

I think another contender might've been "let fsa: [2 of Int]", but if you want a complete list, I'll have to go back and reread the quarter-dozen or so threads on FSAs that pop up whenever we lift the "out of scope" thing (but before they get ruled out of scope again). Semantically speaking, "count * MemoryLayout<Type>.stride" is a pretty much exactly what we're doing at the storage level, so "count * Type" fits well in that regard (IMHO, anyway, since we want the syntax to be concise). It also works as syntactic sugar for declaring homogeneous tuples.

Speaking of which, IIRC, at one point we were considering adding subscripts to tuples and using that for FSAs. I think the hangups were pretty much just that tuples can't conform to protocols and the iffy semantics of subscripting heterogeneous tuples. The former is a bit of a pain point anyway, and unless I've forgotten about some deal-breaker that was brought up in one of those threads, I think it's somewhat likely that we'll eventually do something about it. Unrelated to FSAs and subscripting tuples, we've also discussed variadic generic arguments... My recollection is that there was broad support for the idea; it's just a matter of it being in-scope so that we can figure out the syntax. Once we get that done, if we make tuples extendable we could just say (hand-wavey straw-man syntax, of course):
    extension (Ts: _...) where Ts.count > 0, Ts.reduce(true) { $0 = Ts.Head.self == $1.self } == true {
        subscript(_ i: Int) -> Ts.Head {...}
    }

... and just do it all through tuples. Is this a better than having a dedicated FSA type? Dunno. In large part, it depends on how difficult the various ideas will be to implement, and how useful the extra bits of functionality would be to the rest of the language. Unfortunately, the people who have those answers are busy working with the Swift 4.0 release, and likely don't have time to really weigh in on out of scope topics (which we all agree can be frustrating, but there are practical realities in life, and staying focused enough to meet deadlines is one of theirs).

- Dave Sweeris

···

On Jul 23, 2017, at 09:08, Taylor Swift <kelvin13ma@gmail.com> wrote:

I’ll throw my syntax suggestion into the mix: backslashed brackets for the braces, a colon for the separator.

let fsa: \[3: Int\] = \[3: 1, 2, 3\]

Or maybe go the string interpolation route and only backslash the first bracket.

let fsa: \[3: Int] = \[3: 1, 2, 3]

I think that looks pretty clean. For one-dimensional arrays, you could even omit the size and infer the type.

let fsa = \[1, 2, 3] // Of type \[3: Int]

And I agree with Taylor that the separator chosen should have no standalone use in the language — colon, semicolon, pound sign, etc are OK, but operators shouldn’t be used.

···

On Jul 23, 2017, at 12:08 PM, Taylor Swift via swift-evolution <swift-evolution@swift.org> wrote:

Using the multiplication operator as a separator character seems like an extraordinarily bad idea.

let fsa:[2 * Int] = [2 * 5, 3] // [10, 3] ???

On Sun, Jul 23, 2017 at 11:59 AM, David Sweeris <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:

Sent from my iPhone

On Jul 23, 2017, at 08:45, Taylor Swift via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Sun, Jul 23, 2017 at 5:29 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I wanted to read the proposal, but skipped it as soon as I’ve seen the syntax. From the esthetic point of you the proposed syntax is really ugly. Again I’m not speaking against the feature in general, nor against any of the technical benefits fixed-size array will provide to us. I simply dislike the syntax, which in my opinion does not fit to Swift.

What about a double colon?

let fsa:[5, 2::Int] = [5, 2::[::0, 0]: 5, [::5, 1]: 6, default: -1]

I thought we'd mostly settled on "let fsa: [count * Type]" last time this came up.

- Dave Sweeris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I think fixed-size arrays should be a nominal type like any other. They
should be able to have methods, conform to protocols, be extended with new
behavior, and generally present a user-experience similar to what arrays
already have. In particular, they should conform to Collection and to
ExpressibleByArrayLiteral.

If we are going to use semicolons at all, it should be to demarcate the
rows of a 2-dimensional array, as that will be one of the most common
use-cases:

let identity3x3: [Int(3, 3)] = [1, 0, 0;
                                0, 1, 0;
                                0, 0, 1]

I also think Chris Lattner has a very good plan for initializing fixed-size
arrays, which I will quote here:

1) Fixed size arrays should have an initializer to init all the elements
to some concrete value.
2) They should have an init that takes a closure, and runs it once per
element, passing in the index.
3) Either through a magic value or a third initializer, it should be
possible to *explicitly* create a fixed size array with uninitialized
garbage for the elements. This is important for specific optimizations,
and should also come to Array as well.

IMO, it isn’t a problem that C allows arrays to be uninitialized - the
problem is that it is a really bad default.

-Chris

After all, Swift is safe by default, and users can opt into unsafe
territory when necessary. There are certain algorithms which involve
filling in an array of known length in arbitrary order, and they should be
possible to implement in Swift.

There is still the matter of bikeshedding how to spell the type of a
fixed-size array. I used “[Int(3, 3)]” above, but I am nearly certain that
a better spelling exists.

Nevin

···

On Sat, Jul 22, 2017 at 3:02 PM, Chris Lattner via swift-evolution < swift-evolution@swift.org> wrote:

let fsa:[2 * Int] = [2 * 5, 3] // [10, 3] ???

Correct. If you wanted a multidimensional array, that'd be written "let
nestedFSA: [2*[5*Int]]". Or, speculating a bit, I suppose maybe "let
nestedFSA: [[5*Int]*2]", if we wanted there to be a column-major option.
IMHO all those read better than this proposal's syntax.

No, what I’m saying is does the phrase “[2 * 5, 3]” mean a fixed size array
of length two and with the elements 5 and 3, or a flexible sized array with
two elements 10 and 3? This is v confusing and difficult to read,
especially when you have actual multiplications going on such as

let fsa:[2 * Int] = [2 * 3 * 5, 3] // [15, 3] ???

Using the *multiplication operator* as a *separator* character seems like
an extraordinarily bad idea.

Well, much to the dismay of my occasionally-inner Unicode fanboy, we
decided that we didn't want to make everyone have to figure out how to type
"×" to use a stdlib type (to the further dismay of my O-IUF, this is
actually a really good argument, especially since a likely use for FSA is
in embedded or systems programming, where you might need to use an editor
that doesn't support Unicode).

The text encoding of the source code shouldn’t have any effect on the
compiled target? And while the ◊ or × suggestions are semi-rhetorical,
given a choice between difficult-to-read syntax and unicode, I’ll choose
unicode every time. Swift is nowhere near the first language to use
unconventional symbols. Inserting special characters these days is barely
harder than typing a capital letter. They should be used sparingly but not
ruled out completely, and it doesn’t seem like there are many alternatives.

I think another contender might've been "let fsa: [2 of Int]", but if you
want a complete list, I'll have to go back and reread the quarter-dozen or
so threads on FSAs that pop up whenever we lift the "out of scope" thing
(but before they get ruled out of scope again). Semantically speaking,
"count * MemoryLayout<Type>.stride" is a pretty much exactly what we're
doing at the storage level, so "count * Type" fits well in that regard
(IMHO, anyway, since we want the syntax to be concise). It also works as
syntactic sugar for declaring homogeneous tuples.

If we’re actually going to try and establish a relationship between the FSA
asterisk and the multiplication asterisk, this is even more problematic.
How does the asterisk work in FSA literals, where there is no type
annotation to go on the right of the asterisk?

Also introducing contextual keywords like “of” is going to cause a huge
amount of problems with syntax highlighters considering how often the word
“of” is used as an argument label in Swift. The words “as” and “stride”
already make for some interesting code highlighting, “of” would take it to
a whole new level.

Speaking of which, IIRC, at one point we were considering adding
subscripts to tuples and using that for FSAs. I think the hangups were
pretty much just that tuples can't conform to protocols and the iffy
semantics of subscripting heterogeneous tuples. The former is a bit of a
pain point anyway, and unless I've forgotten about some deal-breaker that
was brought up in one of those threads, I think it's somewhat likely that
we'll eventually do something about it. Unrelated to FSAs and subscripting
tuples, we've also discussed variadic generic arguments... My recollection
is that there was broad support for the idea; it's just a matter of it
being in-scope so that we can figure out the syntax. Once we get that done,
if we make tuples extendable we could just say (hand-wavey straw-man
syntax, of course):
    extension (Ts: _...) where Ts.count > 0, Ts.reduce(true) { $0 =
Ts.Head.self == $1.self } == true {
        subscript(_ i: Int) -> Ts.Head {...}
    }

... and just do it all through tuples. Is this a *better* than having a
dedicated FSA type? Dunno. In large part, it depends on how difficult the
various ideas will be to implement, and how useful the extra bits of
functionality would be to the rest of the language. Unfortunately, the
people who have those answers are busy working with the Swift 4.0 release,
and likely don't have time to really weigh in on out of scope topics (which
we all agree can be frustrating, but there are practical realities in life,
and staying focused enough to meet deadlines is one of theirs).

- Dave Sweeris

I don’t think tuples are a suitable replacement for FSAs. A tuple should be
able to be broken up and optimized by the compiler, and have no contiguity
guarantees in memory.

···

On Sun, Jul 23, 2017 at 2:21 PM, David Sweeris <davesweeris@mac.com> wrote:

On Jul 23, 2017, at 09:08, Taylor Swift <kelvin13ma@gmail.com> wrote:

I think fixed-size arrays should be a nominal type like any other. They should be able to have methods, conform to protocols, be extended with new behavior, and generally present a user-experience similar to what arrays already have. In particular, they should conform to Collection and to ExpressibleByArrayLiteral.

Nominal types have to be completely initialized in their initializer, and I don’t think that should change. But we want to allow arrays to start off uninitialized, instead of a blanket initialization that would be paved over by the real initial data. That’s one reason FSAs are compound types. Another reason is that they’re kind-of low level, like tuples.

As I said in some other responses, the solution to slapping methods/protocols/other-new-interfaces to compound types is to make a strong type-alias of that type and add your interface there. (Once we have strong type-aliases, of course.)

I have fixed-size arrays to be compound types because

If we are going to use semicolons at all, it should be to demarcate the rows of a 2-dimensional array, as that will be one of the most common use-cases:

let identity3x3: [Int(3, 3)] = [1, 0, 0;
                                0, 1, 0;
                                0, 0, 1]

That doesn’t seem scalable to higher dimensions.

···

On Jul 23, 2017, at 2:07 PM, Nevin Brackett-Rozinsky via swift-evolution <swift-evolution@swift.org> wrote:


Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com

9. I don’t see the value in having both nested FSAs and multi-dimensional FSAs. Aren’t they the same thing? For example, in the code snippet

Why does any language with multi-dimensional arrays (like Fortran or Ada) have them? By this standard, no language should have multi-dimensional arrays. They exist because of data modeling. Co-equal coordinates in the model should be co-equal in their representation in the program. Yes, they are implemented the same way underneath. We don’t copy C everywhere, so why not take this opportunity to do better. Also, just using nesting could imply that the intermediate array types have a meaning, but they might not if they’re just implementation quirks and not part of the abstract model.

Nested arrays are not my solution for multi-coordinate indexing; use multi-dimensional arrays for that. I mention nested arrays because:

Nested arrays fundamentally cannot be banned. (What if an element type is hidden behind a type-alias and is conditionally an array type?)
Doesn’t Swift have to resolve the types at some point anyway? If it’s impossible to ban, we can allow it, but still make it unidiomatic. Nested arrays are much messier to write than multidimensional arrays.

The new draft I’m writing downplays nested arrays a lot. They’re mentioned in the section discussing the core element type (nee the inner non-array type).

I need the definition to explain the “inner non-array type”
I need the inner non-array type to explain which pairings of FSAs for reshaping are legal. (And a similar reason for tuple conversion.) Note the two types can have different nesting levels.
I need to explain that empty arrays cannot be an array element type. (Should this be changed? What happens with tuples or structures containing empty tuples/structures as members? What about empty tuples/sturctures in “Array”? Banning empty arrays means we don’t have to worry about every array element being at the same address. The other way to solve this is to make them one byte (or word) long.)

let a = [;1, 2, 3, 4]
assert(a[0] == 1)
assert(a[1] == 2)
assert(a[2] == 3)
assert(a[3] == 4)
let b = a as [2, 2; Int]
assert(b[0, 0] == 1)
assert(b[0, 1] == 2)
assert(b[1, 0] == 3)
assert(b[1, 1] == 4)
let c = a as [2; [2; Int]]
assert(c[0][0] == 1)
assert(c[0][1] == 2)
assert(c[1][0] == 3)
assert(c[1][1] == 4)

There’s three syntaxes which accomplish two unique things. I lean towards disallowing FSA nesting and instead allowing incomplete index lists to partially unnest multidimensional FSAs. Let’s reserve “[][][]...” for flexible array chained dereferencing.

I don’t understand what your incomplete index list idea is. And as I said, the chaining technique is less I desire it and more I can’t ban it and keep Swift features orthogonal.

Incomplete indexing means

let fsa:[5, 2; Int] = [5, 2; 2, 4, 3, 5, 4, 6, 5, 7, 6, 8]
fsa[3] // [2; 5, 7]

this would be the same as writing

let fsa:[5; [2; Int]] = [5; [2; 2, 4], [2; 3, 5], [2; 4, 6], [2; 5, 7], [2; 6, 8]]
fsa[3] // [2; 5, 7]

in your current system. This would obviate the need to nest FSAs for the purpose of extracting entire rows of data.

Allowing partial indexing as you show it privileges one dimension over the others, although they’re supposed to be co-equal and the reason a particular dimension is privileged is due to an implementation detail. Or would you allow incomplete indexing on other dimensions besides the first? Where the true-multi-dimensional <-> nested FSA equivalence wouldn’t help you (because the elements are dispersed in the whole array). With complete generics, this could be a library function:

func removeRow<Dimension: Int, M…: Int, N…: Int, P: Int, T>(array: […M, P, …N; T], row: Int) -> […M, …N; T] where #lengthof(M) == Dimension

11. This should have defined behavior:

let data = [2, 2; 1, 2, 4, 8]
for (i, x) in data.enumerated()
{
    total += x
}

FSAs are compound types, not named types, so there are no methods. (Just like tuples, FSAs don’t follow any protocols.) But since they’re a built-in, I can customize the “for-in” loop to cover all the elements in an implementation-optimized order. (The implementation has the option of multi-threading if it can see there’s no cross-iteration contamination.) Since you can’t control the order without manual looping, the “#indexOf” expression exists to let you know where you are during an iteration loop.

I first used the iteration variable as the operand to “#indexOf” to determine which array instance to track. Then I saw that the for-loop implements its iteration variable as a pattern in the grammar instead of something simpler. And what happens if a wildcard is used as the iteration variable? And would people question why when the pattern has multiple iteration variables, all are equally valid for “#indexOf”? I moved the operand to be the label because we usually don’t have multiple labels on statements (Is that even legal in Swift?) and can optimize which loops need to be indexable.

I really dislike the addition of magical builtin variables. Iterating through a FSA should just take the FSA down one dimension level, so

let data = [2, 3; 1, 2, 3, 4, 8, 12]
for x:[3;] in data
{
    total += x[0] + x[1] + x[2]
}

would be the pattern.

I’m assuming that automatic incomplete indexing would happen here, instead of a flat loop.

The point of the flat for-loop is to eliminate the drudgery of nesting per-dimension for-loops (and re-editing whenever a dimension value or the dimension count changes) and to let the implementation optimize traversal (especially if a processor vector type is used).

···

On Jul 23, 2017, at 12:04 PM, Taylor Swift <kelvin13ma@gmail.com> wrote:
On Sun, Jul 23, 2017 at 3:08 AM, Daryle Walker <darylew@mac.com <mailto:darylew@mac.com>> wrote:


Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com

And other people complained about that!

If I learned anything from Swift evolution, than it is that there are terrifying differences in the perception of things :wink:
— Some want to get rid of object orientation, others are fond of it
— Some want to enforce that "self." has always to be written out, others are asking for ways to skip it in closures
— Some don't want to use mailing lists, others don't want to move to a forum… :wink:

That's one reason why I became sceptical towards the whole evolution process:
Whatever direction Swift takes, it can't make everyone happy — but changing directions all the time imho is worse than a bunch of bad decisions

lol we might as well use a lozenge as the separator

let fsa:[3, 2 <> Int] = [3, 2 <> 1, 2, 3, 4, 5, 6]

Some languages like Pollen even go the unicode route and use an actual
lozenge

let fsa:[3, 2 ◊ Int] = [3, 2 ◊ 1, 2, 3, 4, 5, 6]

this character is actually very easy to type (≤ 3 keystrokes) in both OSX
and Linux, the only two platforms Swift is targeting currently. It’s also a
standard character present in every monospace font.

···

On Sun, Jul 23, 2017 at 12:18 PM, Robert Bennett <rltbennett@icloud.com> wrote:

I’ll throw my syntax suggestion into the mix: backslashed brackets for the
braces, a colon for the separator.

let fsa: \[3: Int\] = \[3: 1, 2, 3\]

Or maybe go the string interpolation route and only backslash the first
bracket.

let fsa: \[3: Int] = \[3: 1, 2, 3]

I think that looks pretty clean. For one-dimensional arrays, you could
even omit the size and infer the type.

let fsa = \[1, 2, 3] // Of type \[3: Int]

And I agree with Taylor that the separator chosen should have no
standalone use in the language — colon, semicolon, pound sign, etc are OK,
but operators shouldn’t be used.

On Jul 23, 2017, at 12:08 PM, Taylor Swift via swift-evolution < > swift-evolution@swift.org> wrote:

Using the *multiplication operator* as a *separator* character seems like
an extraordinarily bad idea.

let fsa:[2 * Int] = [2 * 5, 3] // [10, 3] ???

On Sun, Jul 23, 2017 at 11:59 AM, David Sweeris <davesweeris@mac.com> > wrote:

Sent from my iPhone

On Jul 23, 2017, at 08:45, Taylor Swift via swift-evolution < >> swift-evolution@swift.org> wrote:

On Sun, Jul 23, 2017 at 5:29 AM, Adrian Zubarev via swift-evolution < >> swift-evolution@swift.org> wrote:

I wanted to read the proposal, but skipped it as soon as I’ve seen the
syntax. From the esthetic point of you the proposed syntax is really ugly.
Again I’m not speaking against the feature in general, nor against any of
the technical benefits fixed-size array will provide to us. I simply
dislike the syntax, which in my opinion does not fit to Swift.

What about a double colon?

let fsa:[5, 2::Int] = [5, 2::[::0, 0]: 5, [::5, 1]: 6, default: -1]

I thought we'd mostly settled on "let fsa: [count * Type]" last time this
came up.

- Dave Sweeris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

let fsa:[2 * Int] = [2 * 5, 3] // [10, 3] ???

Correct. If you wanted a multidimensional array, that'd be written "let nestedFSA: [2*[5*Int]]". Or, speculating a bit, I suppose maybe "let nestedFSA: [[5*Int]*2]", if we wanted there to be a column-major option. IMHO all those read better than this proposal's syntax.

No, what I’m saying is does the phrase “[2 * 5, 3]” mean a fixed size array of length two and with the elements 5 and 3, or a flexible sized array with two elements 10 and 3? This is v confusing and difficult to read, especially when you have actual multiplications going on such as

let fsa:[2 * Int] = [2 * 3 * 5, 3] // [15, 3] ???

That's... huh? To me, "[2 * 3 * 5, 3]" should obviously evaluate to "[30, 3]". How are you getting that "[2*5*3, 3]" could be a 2-element FSA containing 15 and 3? Are you suggesting that instead of "[value * value * value, value]", it could be parsed as "[modifier value * value, value]" (with `modifier` being "2 *")? To me, that syntax would strongly suggest that the modifier only applies to the first element of the array, which would mean the only other option for parsing it would be equivalent to "[[3, 5], 3]", which is neither a match for fsa's type, nor a semantically valid array (the elements have to be the same type), nor a syntactically valid array (the nested array in the first element is missing its "[]").

Using the multiplication operator as a separator character seems like an extraordinarily bad idea.

Well, much to the dismay of my occasionally-inner Unicode fanboy, we decided that we didn't want to make everyone have to figure out how to type "×" to use a stdlib type (to the further dismay of my O-IUF, this is actually a really good argument, especially since a likely use for FSA is in embedded or systems programming, where you might need to use an editor that doesn't support Unicode).

The text encoding of the source code shouldn’t have any effect on the compiled target? And while the ◊ or × suggestions are semi-rhetorical, given a choice between difficult-to-read syntax and unicode, I’ll choose unicode every time. Swift is nowhere near the first language to use unconventional symbols. Inserting special characters these days is barely harder than typing a capital letter. They should be used sparingly but not ruled out completely, and it doesn’t seem like there are many alternatives.

I would choose the unicode route for my projects as well, but locking stdlib types or functionality behind unicode has been a no-go ever since Swift was open-sourced (and presumably before then as well, since nothing in the stdlib uses them). We couldn't even get the unicode operators for set notation: "∩", "∪", "⊂", "⊃", "⊆", "⊇", "∈", "∋", and "∖" (maybe not "∖"... it's been a while), accepted as alternate spellings for: "setA.intersection(setB)", "setA.union(setB)", etc, back in the "Set Algebra" proposal. I really doubt a proposal for FSAs with syntax involving unicode will be accepted. Feel free to try, though. Maybe there's been a change of heart since the topic last came up.

I think another contender might've been "let fsa: [2 of Int]", but if you want a complete list, I'll have to go back and reread the quarter-dozen or so threads on FSAs that pop up whenever we lift the "out of scope" thing (but before they get ruled out of scope again). Semantically speaking, "count * MemoryLayout<Type>.stride" is a pretty much exactly what we're doing at the storage level, so "count * Type" fits well in that regard (IMHO, anyway, since we want the syntax to be concise). It also works as syntactic sugar for declaring homogeneous tuples.

If we’re actually going to try and establish a relationship between the FSA asterisk and the multiplication asterisk, this is even more problematic. How does the asterisk work in FSA literals, where there is no type annotation to go on the right of the asterisk?

Is there a reason the existing type inference system wouldn't work?

Also introducing contextual keywords like “of” is going to cause a huge amount of problems with syntax highlighters considering how often the word “of” is used as an argument label in Swift. The words “as” and “stride” already make for some interesting code highlighting, “of” would take it to a whole new level.

I'm not sure we should be basing decisions like this on potential bugs in the syntax highlighting systems of various IDEs.

Speaking of which, IIRC, at one point we were considering adding subscripts to tuples and using that for FSAs. I think the hangups were pretty much just that tuples can't conform to protocols and the iffy semantics of subscripting heterogeneous tuples. The former is a bit of a pain point anyway, and unless I've forgotten about some deal-breaker that was brought up in one of those threads, I think it's somewhat likely that we'll eventually do something about it. Unrelated to FSAs and subscripting tuples, we've also discussed variadic generic arguments... My recollection is that there was broad support for the idea; it's just a matter of it being in-scope so that we can figure out the syntax. Once we get that done, if we make tuples extendable we could just say (hand-wavey straw-man syntax, of course):
    extension (Ts: _...) where Ts.count > 0, Ts.reduce(true) { $0 = Ts.Head.self == $1.self } == true {
        subscript(_ i: Int) -> Ts.Head {...}
    }

... and just do it all through tuples. Is this a better than having a dedicated FSA type? Dunno. In large part, it depends on how difficult the various ideas will be to implement, and how useful the extra bits of functionality would be to the rest of the language. Unfortunately, the people who have those answers are busy working with the Swift 4.0 release, and likely don't have time to really weigh in on out of scope topics (which we all agree can be frustrating, but there are practical realities in life, and staying focused enough to meet deadlines is one of theirs).

I don’t think tuples are a suitable replacement for FSAs. A tuple should be able to be broken up and optimized by the compiler, and have no contiguity guarantees in memory.

C's static arrays are imported as tuples, which need at least some level of contiguity guarantees to work. Even if tuples in general make that such a guarantee, tuple-based FSAs could use the same compiler logic as imported C arrays.

- Dave Sweeris

···

On Jul 23, 2017, at 12:18, Taylor Swift <kelvin13ma@gmail.com <mailto:kelvin13ma@gmail.com>> wrote:

On Sun, Jul 23, 2017 at 2:21 PM, David Sweeris <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:
On Jul 23, 2017, at 09:08, Taylor Swift <kelvin13ma@gmail.com <mailto:kelvin13ma@gmail.com>> wrote:

Terms of Service

Privacy Policy

Cookie Policy