[swift-evolution] [RFC] Definitive Initialization and Incompatibilities with Fixed-size Arrays

Daryle Walker darylew at mac.com
Sat Jul 22 17:03:53 CDT 2017

> On Jul 22, 2017, at 3:02 PM, Chris Lattner <clattner at nondot.org> wrote:
> On Jul 18, 2017, at 1:00 PM, Daryle Walker via swift-evolution <swift-evolution at swift.org> wrote:
>>> On Jul 17, 2017, at 3:26 AM, Félix Cloutier <felixcca at yahoo.ca> wrote:
>>> I think that you're getting ahead of yourself. Fixed-size arrays are still useful even if they have to have been demonstrably initialized before you can use dynamic indices with them. A ton of people have already gotten into the habit of writing `int foo[40] = {}` in C.
>> The person with the initial suggestion regrets this habit and deliberately doesn’t want it. In other words, don’t support the waste of cycles to pre-initialize just for the elements to be immediately paved over with the real initial data. And we may make arrays of types that are heavy to initialize and may not have a default initializer, so even using a default-value term at declaration will lead to a big waste of cycles.
>> We eventually have to decide how to modify deterministic initialization. Do we take the safe path with dynamic deterministic initialization (and add extra resources)? Or the fast path with suspension of checks (and risk undefined behavior)? We’re probably going to lean toward the former, especially since we can limit its scope (and therefore cost).

Oh, I just posted that I just updated my proposal before I saw this in my in-box.

> In my opinion, there is an easy three step plan :-) to solving this problem, riffing on Array:

Well, fixed-size arrays don’t have initializers, for the same reason tuples don’t: they’re compound types instead of named types and they literally have nowhere to place initializer definitions. But like tuples, FSAs have a literal syntax that works as a substitute for full-blown initializers. My recent update made FSA-literals distinct from standard array literals.

Should there be an equivalent to “ExpressibleByArrayLiteral” for multi-dimensional literals? I don’t know, but it could wait for version 2.

> 1) Fixed size arrays should have an initializer to init all the elements to some concrete value.

let a = [4; default: “whatever”]  // [4; String]

> 2) They should have an init that takes a closure, and runs it once per element, passing in the index.

let b = [2, 3; func: { Double($0.0 * $0.0 + $0.1 * $0.1) }]  // [2, 3; Double]

The index, “$0” here, is passed as “[2; Int]”.

> 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.

let c = [6; 1, 2, 3]  // [6; Int], last 3 elements uninitialized

Obviously, the literal can’t use a “default” or “func” term if you want to keep some elements uninitialized.

Leaving elements uninitialized is a big reason for leaving FSAs compound types. We should not violate the expectation that an initializer (which named types have) leaves all sub-objects initialized. So I’m against adding this feature to Array, unless you mean something like “size” vs. “capacity” in C++’s vector and you need to call something like “push_back” to add an element instead of directly using subscript. Since a FSA is a compound type, its sub-objects’ states are tracked with deterministic initialization, just like tuple members.

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

I was struggling which way to go; add run-time deterministic initialization or allow undefined behavior. For now, it’s neither; the current compile-time deterministic initialization has to be followed. But compile-time DI isn’t big a setback as long as you have all the information you need to set every element before initialization-assignment (with a function term).

I read a post once wishing for a function to join tuples together as a giant tuple. I was wondering about the same thing for FSAs, where you have to specify which index you want to be the axis of the join (and all the non-axis dimensions have to have corresponding lengths being equal). This requires more advanced generics than we have now; but if implemented, we could build arrays in piecemeal then join them together for the final value. It could look like:

let d = joinArrays<2>( [2, 3, 7, 5, 9; default: 23], [2, 3, 4, 5, 9; func: { $0.0 + $0.4 }] )  // [2, 3, 11, 5, 9; Int], axis 2: 7 + 4 == 11

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

More information about the swift-evolution mailing list