IMHO if var val: T = .{ ... }; becomes the ‘one-true-init-style’ than the ‘type-placeholder-dot’ becomes even harder to justify / explain
E.g. it should be var val: T = { ... }.
I also have issues with @splat, since it’s even more ‘@-builtin-noise’, and initializing an array should be covered by syntax IMHO (if that syntax is just sugar for a builtin @splat - fine with me.
E.g. instead of var arr: [8]T = @splat(.{}); it should just be var arr: [8]T = .{}; but I think that proposal had already been killed. Array initialization syntax is still the one thing that C99 does better than Zig.
But IMHO a general splat is too restricted since it just writes the whole array, but often I’d like to initialize some specific array items (not necessarily at the start of the array, and not necessarily with increasing indices - because the indices might come from some code-generation step) to specific values and fill up the rest with defaults. Currently this requires writing an init-blocks, which also isn’t great because I need to return an array in exactly the right size - unless the array is nested in a struct like here:
If the .attrs array would not be nested in a VertexLayoutState that init block would be even more awkward.
Overall, I don’t dislike the .{} syntax, as {} signifies a block expression, while .{} represents a compound literal, and they shouldn’t be confused.
However, the OP’s approach is quite interesting. He essentially proposes using [] to represent compound literals, to avoid confusion with the {} block expression, while also eliminating the . syntax that many people dislike.
Personally, I think the main concern here is whether [] will be confused with the use of indexes and subscripts. For now, I don’t have too many complaints about the use of .{}. I’m more concerned about whether the syntax is consistent and easy to confuse with other syntax, rather than whether there are any symbols that make people uncomfortable with the literal meaning.
It would actually be nice to allow unlabeled blocks which could return a value with just break val; instead of break :label val; because 99% of the time one just needs to break out of the current block and not skip to a specific label.
If [] were used to represent compound literals, this would mean that the usage of slices and indices would have to change, perhaps to .[].
Subtly, this would seem to more closely tie access to index values to the way structure members are accessed: . always accesses a member or declaration of a compound literal, and .[start..end] is like calling a method of the slice built-in constructor.
This idea is a bit crazy, and its main obstacle is the significant breaking factor.
And, the requirement you want seems to be a @splatWithIndex
Nah, the C99 array initialization syntax would do the job with much less syntax noise, the important part here, the array indices are code-generated constants and don’t need to be in order or consecutive, and the whole nested array has more slots than are initialized. The unmentioned slots are default-initialized to zero or defaults from the struct declaration:
…or alternatively Odin:
…or D:
These (C99, Odin, D) are the three main-styles for this types of nested array initialization I’ve seen, most other recent languages don’t have this type of initialization at all.
This style of C99 essentially relies on the convention that “undefined items are implicitly initialized to 0”, which violates the zig philosophy that explicit is better than implicit. There is no silver bullet, and we must admit that when we choose a philosophy, we have to give up the convenience of another.
Zig already does have default-initialization via .{} if the struct declaration has default values for all struct items. If an array item doesn’t have a complete default initialization it would be an error just like in regular structs which don’t default-initialize all items. I don’t see a conflict with Zig’s uninitialized philosophy in this case.
E.g. the remaining array items would be ‘filled up’ with .{}. Alternatively Zig could have explicit syntax like Rust’s ..Default::default(), but that looks really noisy very quickly, look at all those trailing ..Default::default() here:
Whether the default values are zero or something else doesn’t matter at all in this case. In Zig the array items would be filled up with default values coming from the struct declarations, not with zero, and if anything ends up being uninitialized because no defaults are declared, it would be an ‘x is uninitialized’ compile error.
Perhaps we have a different understanding of what “explicit” means. To me, “explicit” means there are no “conventional magic values.” When initialized with .{}, the default value is clearly stated in the definition of the composite type. Setting the default value to 0 without explicit declaration is undoubtedly a “conventional magic” in my opinion, because 0 should not have any special status in principle.
There is no implicit zero-initialization like in C anywhere. If the array item struct can’t be initialized with .{} because the struct declaration is missing default values, it would be an ‘uninitialized’ compile error - which is consistent with Zig’s existing regular behaviour when initializing a struct.
I roughly understand your point.
For [i]Structure, you’d like to be able to directly use .{} instead of @splat(.{}).
Furthermore, for [i][j]Structure, .{} could be used to express @splat(@as([j]Structure, @splat(.{}))).
However, this could cause some inconsistencies: for child types like [i]uszie, which aren’t expressible as composite literals, this wouldn’t work.
Another issue is that zig currently encourages the use of .empty and .init as default values rather than .{}.
Yeah tbh I don’t like that either because it’s inconsistent with regular struct initialization. E.g. .{} is just more natural for a default-initialized struct in the sense of not overriding any of the defaults, e.g.:
Override the defaults for .x and .y:
const a: T = .{ .x = 23, .y = 34 };
…only override the default for .x:
const a: T = .{ .x = 23 };
…and for the next step to not override any of the defaults .{} makes a lot more sense than .init, .empty.default (and this also opens up the new can of worms what this should be called - it would be purely by convention)
The idea of .init / .empty makes sense if you need different initialization states to pick from though - e.g. if .empty has different values than .init - but it’s just a convention, not syntax.
However, this could cause some inconsistencies: for child types like [i]usize , which aren’t expressible as composite literals, this wouldn’t work.
This would be consistent to fail because a type like usize doesn’t have a way to define default value. I don’t see a problem with this behaviour tbh.