www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.ideas - Inferring static array size

reply NotYouAgain <NotYouAgain gmail.com> writes:
The idea is simple, really, really simple.

Instead of this:

  int[5] a = [1, 2, 3, 4, 5];

allow this:

  int[$_] a = [1, 2, 3, 4, 5];

Both are static arrays.

Having to change the length everytime i change the initialisation 
list is a pain in the $____

The compiler infers the size in the second example, based on the 
number of elements in the initialisation list. Just like C can do.
Apr 28
next sibling parent reply =?UTF-8?B?Q2hsb8Op?= <chloekek use.startmail.com> writes:
On 4/28/24 11:31, NotYouAgain wrote:
 The idea is simple, really, really simple.
 
 Instead of this:
 
   int[5] a = [1, 2, 3, 4, 5];
 
 allow this:
 
   int[$_] a = [1, 2, 3, 4, 5];
 
 Both are static arrays.
 
 Having to change the length everytime i change the initialisation list 
 is a pain in the $____
 
 The compiler infers the size in the second example, based on the number 
 of elements in the initialisation list. Just like C can do.
 
The Phobos function staticArray can be used to infer the array length while explicitly specifying (or inferring) the element type: import std.array : staticArray; auto a = [1, 2, 3, 4, 5].staticArray; // inferred int[5] auto b = [1, 2, 3, 4, 5].staticArray!float; // inferred float[5]
Apr 28
parent reply NotYouAgain <NotYouAgain gmail.com> writes:
On Sunday, 28 April 2024 at 09:35:22 UTC, Chloé wrote:
 The Phobos function staticArray can be used to infer the array 
 length while explicitly specifying (or inferring) the element 
 type:

     import std.array : staticArray;
     auto a = [1, 2, 3, 4, 5].staticArray;  // inferred int[5]
     auto b = [1, 2, 3, 4, 5].staticArray!float;  // inferred 
 float[5]
Oh. interesting, I didn't know. Thanks. but... auto a = 20.iota.array.staticArray; // Error: none of the overloads of template `std.array.staticArray` are callable using argument types `!()(int[])`
Apr 28
parent reply =?UTF-8?B?Q2hsb8Op?= <chloekek use.startmail.com> writes:
On 4/28/24 11:43, NotYouAgain wrote:
 On Sunday, 28 April 2024 at 09:35:22 UTC, Chloé wrote:
 The Phobos function staticArray can be used to infer the array length 
 while explicitly specifying (or inferring) the element type:

     import std.array : staticArray;
     auto a = [1, 2, 3, 4, 5].staticArray;  // inferred int[5]
     auto b = [1, 2, 3, 4, 5].staticArray!float;  // inferred float[5]
Oh. interesting, I didn't know. Thanks. but... auto a = 20.iota.array.staticArray;     //  Error: none of the overloads of template `std.array.staticArray` are callable using argument types `!()(int[])`
To CTFE a range into a static array, try: staticArray!(20.iota) Note the exclamation mark. This uses the following overload: auto staticArray(alias a)() if (isInputRange!(typeof(a)))
Apr 28
parent NotYouAgain <NotYouAgain gmail.com> writes:
On Sunday, 28 April 2024 at 09:50:05 UTC, Chloé wrote:
 To CTFE a range into a static array, try:

     staticArray!(20.iota)

 Note the exclamation mark. This uses the following overload:

     auto staticArray(alias a)()
         if (isInputRange!(typeof(a)))
Yes that works. Thanks. Not as intuitive as [$_] would be, but still a usable solution. At least I don't have to put my array in a separate module for it to work ;-)
Apr 28
prev sibling next sibling parent Mike Parker <aldacron gmail.com> writes:
On Sunday, 28 April 2024 at 09:31:01 UTC, NotYouAgain wrote:
 The idea is simple, really, really simple.

 Instead of this:

  int[5] a = [1, 2, 3, 4, 5];

 allow this:

  int[$_] a = [1, 2, 3, 4, 5];

 Both are static arrays.

 Having to change the length everytime i change the 
 initialisation list is a pain in the $____

 The compiler infers the size in the second example, based on 
 the number of elements in the initialisation list. Just like C 
 can do.
FYI, a DIP for this was submitted and withdrawn by the author in response to negative feedback. See below, and particularly the "Reviews" section at the bottom of the document. https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1039.md
Apr 28
prev sibling parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
On Sunday, 28 April 2024 at 09:31:01 UTC, NotYouAgain wrote:
 The idea is simple, really, really simple.

 Instead of this:

  int[5] a = [1, 2, 3, 4, 5];

 allow this:

  int[$_] a = [1, 2, 3, 4, 5];

 Both are static arrays.

 Having to change the length everytime i change the 
 initialisation list is a pain in the $____

 The compiler infers the size in the second example, based on 
 the number of elements in the initialisation list. Just like C 
 can do.
YES please, this is long overdue, there is no reason to not have this However, i don't like the proposed syntax, it should be 1 character to type, and should not be a ugly one `_` should be enough
Apr 28
next sibling parent NotYouAgain <NotYouAgain gmail.com> writes:
On Sunday, 28 April 2024 at 10:17:06 UTC, ryuukk_ wrote:
 YES please, this is long overdue, there is no reason to not 
 have this

 However, i don't like the proposed syntax, it should be 1 
 character to type, and should not be a ugly one

 `_` should be enough
So my thinking behind [$_] is: '$' is already a synonym for the length of the array. e.g int[2] arr = [1,2]; writeln(arr[0..arr.length]); // [1, 2] writeln(arr[0..$]); // [1, 2] so with [$_] ... '$_' means 'array length' and '_' means.. please compiler fill in missing blank. true, [_] can just as easily mean .. please compiler fill in the missing blank. Frankly I don't mind either. [_] is easier to type, but not as easy on the eye I think ;-) ..looks kinda like a box in my ide.
Apr 28
prev sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 28/04/2024 10:17 PM, ryuukk_ wrote:
 |_| should be enough
``_`` is a valid identifier that may point to a length. So that isn't usable.
Apr 28
parent reply electricface <electricface qq.com> writes:
On Sunday, 28 April 2024 at 16:08:11 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 On 28/04/2024 10:17 PM, ryuukk_ wrote:
 |_| should be enough
``_`` is a valid identifier that may point to a length. So that isn't usable.
I think it's better to represent the automatically calculated length using `..` . ``` int[..] a = [1,2,3,4,5] ```
Apr 29
parent reply NotYouAgain <NotYouAgain gmail.com> writes:
On Tuesday, 30 April 2024 at 02:20:39 UTC, electricface wrote:
 On Sunday, 28 April 2024 at 16:08:11 UTC, Richard (Rikki) 
 Andrew Cattermole wrote:
 On 28/04/2024 10:17 PM, ryuukk_ wrote:
 |_| should be enough
``_`` is a valid identifier that may point to a length. So that isn't usable.
I think it's better to represent the automatically calculated length using `..` . ``` int[..] a = [1,2,3,4,5] ```
Yes, I like this more. Someone please submit a DIP.
Apr 29
parent reply rkompass <rkompass gmx.de> writes:
On Tuesday, 30 April 2024 at 05:51:24 UTC, NotYouAgain wrote:
 On Tuesday, 30 April 2024 at 02:20:39 UTC, electricface wrote:
 On Sunday, 28 April 2024 at 16:08:11 UTC, Richard (Rikki) 
 Andrew Cattermole wrote:
 On 28/04/2024 10:17 PM, ryuukk_ wrote:
 |_| should be enough
``_`` is a valid identifier that may point to a length. So that isn't usable.
I think it's better to represent the automatically calculated length using `..` . ``` int[..] a = [1,2,3,4,5] ```
Yes, I like this more. Someone please submit a DIP.
I actually find this counterintuitive. Because [..] in my intuition looks like a dynamic array. But it's about creating a static array. How about
May 03
parent reply NotYouAgain <NotYouAgain gmail.com> writes:
On Friday, 3 May 2024 at 07:48:01 UTC, rkompass wrote:
 I actually find this counterintuitive. Because [..] in my 
 intuition looks like a dynamic array. But it's about creating a 
 static array.
 ...
[..] is a slice thing - it's not related to how the array came into existence. i.e it can work on a static array, and a dynamic array (see code example below). Perhaps for that reason, [..] might not be suitable after all. perhaps [$] is still the best, since $ *always* simply refers to the length of the array. so: int[4] // 4 is the length int[$] // $ is still the length, you just don't know what it is yet. module m; safe: private: import std; void main() { auto a = [1, 2, 3, 4, 5].staticArray; //auto a[$] = [1, 2, 3, 4, 5]; // the compiler can infer the 'length' writeln(fold!((a, b) => a + b)(a[0..2])); // 3 (see: [..] used on a static array. }
May 03
parent reply rkompass <rkompass gmx.de> writes:
 perhaps [$] is still the best, since $ *always* simply refers 
 to the length of the array.

 so:

 int[4] // 4 is the length
 int[$] // $ is still the length, you just don't know what it is 
 yet.

 module m;
  safe:
 private:
 import std;

 void main()
 {
     auto a = [1, 2, 3, 4, 5].staticArray;
     //auto a[$] = [1, 2, 3, 4, 5]; // the compiler can infer 
 the 'length'

     writeln(fold!((a, b) => a + b)(a[0..2])); // 3  (see: [..] 
 used on a static array.
 }
This, of course, is even better. And now we are exactly at DIP1039: https://forum.dlang.org/thread/ucqyqkvaznbxkasvdjpx forum.dlang.org?page=1 I read through the discusion there and found it quite digressing and not productive. There was no real weighing the pros against the cons. Of course we have staticArray, but in programming it's much about conciseness which at the same time allows for effortlessy (intuitively) transporting the meaning of a construct. And this criterion here is fulfilled. No new symbol is introduced, just a meaning extended, which is immediatly clear. Could DIP 1039 be restarted?
May 03
next sibling parent reply Nick Treleaven <nick geany.org> writes:
On Friday, 3 May 2024 at 09:15:02 UTC, rkompass wrote:
 I read through the discusion there and found it quite 
 digressing and not productive.
 There was no real weighing the pros against the cons.

 Of course we have staticArray, but in programming it's much 
 about conciseness which at the same time allows for effortlessy 
 (intuitively) transporting the meaning of a construct.
 And this criterion here is fulfilled.
 No new symbol is introduced, just a meaning extended, which is 
 immediatly clear.

 Could DIP 1039 be restarted?
I think the sticking point is making an implementation that does not complicate the compiler code much.
May 03
next sibling parent reply NotYouAgain <NotYouAgain gmail.com> writes:
On Friday, 3 May 2024 at 10:04:57 UTC, Nick Treleaven wrote:
 On Friday, 3 May 2024 at 09:15:02 UTC, rkompass wrote:
 I read through the discusion there and found it quite 
 digressing and not productive.
 There was no real weighing the pros against the cons.

 Of course we have staticArray, but in programming it's much 
 about conciseness which at the same time allows for 
 effortlessy (intuitively) transporting the meaning of a 
 construct.
 And this criterion here is fulfilled.
 No new symbol is introduced, just a meaning extended, which is 
 immediatly clear.

 Could DIP 1039 be restarted?
I think the sticking point is making an implementation that does not complicate the compiler code much.
Yes, i was thinking to post the exact same comment actually ;-) As it is, CTFE using .staticArray is a useful alternative... unless you're trying to do something like this: module m; safe: private: import std; private enum numItems = 10; class myClass {} void main() { // Object[numItems] objectArr = generate!(() => new myClass())().take(numItems).array; import std.array : staticArray; auto arr = generate!(() => new myClass())().take(numItems).staticArray; // nope }
May 03
next sibling parent reply NotYouAgain <NotYouAgain gmail.com> writes:
On Friday, 3 May 2024 at 11:45:27 UTC, NotYouAgain wrote:
 ...
and this won't work either: int i = 2; //int[10] powersOfTwo = generate!(() => i *= 2)().take(10).array; // fine auto powersOfTwo = staticArray!(generate!(() => i *= 2)().take(10).array); // nope
May 03
next sibling parent reply rkompass <rkompass gmx.de> writes:
On Friday, 3 May 2024 at 12:12:29 UTC, NotYouAgain wrote:
 On Friday, 3 May 2024 at 11:45:27 UTC, NotYouAgain wrote:
 ...
and this won't work either: int i = 2; //int[10] powersOfTwo = generate!(() => i *= 2)().take(10).array; // fine auto powersOfTwo = staticArray!(generate!(() => i *= 2)().take(10).array); // nope
This can be seen as a flaw of staticArray.?. From my perspective it's more about having the ability to create self-evident code: ``` int[] integers = [0..20]; int[$] staticInts = [0..20]; int[] evens = [0,2,..,20]; int[$] staticEvens = [0,2,..,20]; ``` or something similarly obvious would be on my wishlist. Of course this would not allow for powers of two, which is yet a step more involved.
May 03
parent NotYouAgain <NotYouAgain gmail.com> writes:
On Friday, 3 May 2024 at 12:49:33 UTC, rkompass wrote:
 ..
 This can be seen as a flaw of staticArray.?.> ...
My objective was not to see what 'flaws' i could uncover in staticArray, but to explore all possible interactions with staticArray.. so as to examine it further. As it is, as long as staticArray is there, there is zero chance of a DIP being accepted. I didn't know staticArray was there when I posted this in DIP ideas ;-) Even if it wasn't there, someone would likely have created it in response to this DIP idea... So I think this thread will go the way of the dodo now....
May 03
prev sibling parent reply Nick Treleaven <nick geany.org> writes:
On Friday, 3 May 2024 at 12:12:29 UTC, NotYouAgain wrote:
 and this won't work either:

 int i = 2;
 //int[10] powersOfTwo = generate!(() => i *= 
 2)().take(10).array; // fine
 auto powersOfTwo = staticArray!(generate!(() => i *= 
 2)().take(10).array); // nope
You can't read `i` at compile-time. `staticArray!range` only works when `range` elements are known at compile-time. ```d auto powersOfTwo = staticArray!10(generate!(() => i *= 2)()); pragma(msg, typeof(powersOfTwo)); // int[10] ```
May 03
parent reply rkompass <rkompass gmx.de> writes:
On Friday, 3 May 2024 at 18:27:56 UTC, Nick Treleaven wrote:
  `staticArray!range` only works when `range` elements are known 
 at compile-time.

 ```d
 auto powersOfTwo = staticArray!10(generate!(() => i *= 2)());
 pragma(msg, typeof(powersOfTwo)); // int[10]
 ```
Thanks for the many nice explanations. I now have the feeling I get the point. I tried and found that: ```d auto c = 5.iota.staticArray!5; // works ``` My interpretation is: `iota()` is a lazy range function (forward range??). It's laziness implies that the point of termination (and thus the length) is not known at the start. Which is a requirement for `staticArray`. Now the questions arise: 1. Could an alternative to staticArray be coded that stores the elements in a dynamic array at compile time and then convert that to a static array? 2. Would it make sense to add a range type that is lazy, but at the same time provides/transports information about its length/size? 3. Would it perhaps be possible to code a better `iota()` that has this ability? These are learners questions. I hope you don't mind. They belong to this topic, I suppose. Thank you.
May 04
parent Nick Treleaven <nick geany.org> writes:
On Saturday, 4 May 2024 at 07:17:49 UTC, rkompass wrote:
 I tried and found that:
 ```d
 auto c = 5.iota.staticArray!5;        // works
 ```

 My interpretation is: `iota()` is a lazy range function 
 (forward range??).
 It's laziness implies that the point of termination (and thus 
 the length) is not known at the start.
 Which is a requirement for `staticArray`.

 Now the questions arise:

 1. Could an alternative to staticArray be coded that stores the 
 elements in a dynamic array at compile time and then convert 
 that to a static array?
It already supports that: ```d auto a = staticArray!(iota(5)); pragma(msg, typeof(a)); // int[5] ```
 2. Would it make sense to add a range type that is lazy, but at 
 the same time provides/transports information about its 
 length/size?
Maybe.
 3. Would it perhaps be possible to code a better `iota()` that 
 has this ability?
Maybe, although the elements of iota are known at compile-time too, so you can just do `enum r = iota(5);` and then pass it as a runtime argument to a function expecting a range.
May 04
prev sibling parent Nick Treleaven <nick geany.org> writes:
On Friday, 3 May 2024 at 11:45:27 UTC, NotYouAgain wrote:
 As it is, CTFE using .staticArray is a useful alternative... 
 unless you're trying to do something like this:

 module m;
  safe:
 private:

 import std;

 private enum numItems = 10;

 class myClass {}

 void main()
 {
     // Object[numItems] objectArr = generate!(() => new 
 myClass())().take(numItems).array;
Note: that works because you are giving the static array the length information at compile time.
     import std.array : staticArray;
     auto arr = generate!(() => new 
 myClass())().take(numItems).staticArray; // nope
 }
The compiler would not be able to infer the length of the range even if the DIP had been accepted. The range produced by `take` does not have access to the length at compile-time. This does work: ```d auto arr = generate!(() => new myClass())().staticArray!numItems; pragma(msg, typeof(arr)); // myClass[10] ```
May 03
prev sibling next sibling parent rkompass <rkompass gmx.de> writes:
On Friday, 3 May 2024 at 10:04:57 UTC, Nick Treleaven wrote:
 I think the sticking point is making an implementation that 
 does not complicate the compiler code much.
I'm not familiar with the compiler programmers side, but as I see it (correct me if I'm wrong), the DIP included a PR that already had the compiler changes in it. To me the feedback thread to DIP1039 was partly strange and inconclusive.
May 03
prev sibling parent reply NotYouAgain <NotYouAgain gmail.com> writes:
On Friday, 3 May 2024 at 10:04:57 UTC, Nick Treleaven wrote:
 ..
 ..
 I think the sticking point is making an implementation that 
 does not complicate the compiler code much.
The other point (apart from your point) I wanted to make, but forgot, was that even if a DIP is accepted, that does NOT mean it is mandatory that it be implemented (at least not as far as I know). Also, since Walter, like pretty much everyone else, volunteer his time and effort, mandating that an accepted DIP be implemented, would seem rather silly - I mean volunteers to not succumb to such mandates. For that reason, and DIP probably should have the accompanied implementation, especially if it's a DIP that Walter doesn't like, or is not inclined to work on. Unfortunately, compilers are a pretty complicated beast ;-(
May 04
parent Stefan Koch <uplink.coder googlemail.com> writes:
On Saturday, 4 May 2024 at 08:38:31 UTC, NotYouAgain wrote:
 On Friday, 3 May 2024 at 10:04:57 UTC, Nick Treleaven wrote:
 [...]
For that reason, and DIP probably should have the accompanied implementation, especially if it's a DIP that Walter doesn't like, or is not inclined to work on. Unfortunately, compilers are a pretty complicated beast ;-(
There have been implementations for this feature before, if you do not want to reuse those. I could donate some of my time and implement it.
May 04
prev sibling parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Friday, 3 May 2024 at 09:15:02 UTC, rkompass wrote:
 Could DIP 1039 be restarted?
I don’t know why it couldn’t. One thing where DIP1039 would shine over `staticArray`: String literals (typed `immutable(Char)[]`) are zero terminated. The zero isn’t part of the slice, but it’s there, so one can pass `"blah".ptr` to a C API and be good, because `"blah"` isn’t `['b', 'l', 'a', 'h']`, but rather `['b', 'l', 'a', 'h', '\0'][0..4]`. If you write: ```d auto blah = "blah".staticArray; ``` As far as I can tell, `blah` is an array of 4 chars. It has no zero terminator, and I don’t see how it could have one. However, ```d immutable(char)[$] blah = "blah"; ``` could absolutely be implemented as making space for the 4 characters plus a zero terminator, but `blah.length` would still be 4 and it would be a normal static array otherwise, but contrary to the `staticArray` solution, you could pass `blah.ptr` to a C API. However, you can’t pass a *copy* of `blah` to a C API, as a copy only copies 4 bytes. To be precise: Let `Char` denote any character type; if `arr` is a static array initialized by the `Char[$]` and a string literal, `arr.ptr[a.length]` is `Char(0)`. That rule does not apply to non-character element types (e.g. `int`), and it does not apply to an array object spelled out as a list, e.g. in `['x']`, or gained from a function call. This is already the behavior of `Char[]`s. One might add that the zero terminator not being part of the array might be incorrect, as in C, it is part of the array. My solution would be to add another construct: ```d immutable(char)[$+1] str = "Hello"; ``` The `$+1` is core syntax. There is no `$+2` or anything. It expresses that the array is (at least) 1 character longer than the initializer looks like. This is *exactly* what C does. It’s the maximally faithful translation of: ```c const char str[] = "Hello"; ``` It only exists for character arrays and they must be initialized by a string literal or by a compile-time known `const(char)[]` ending with the zero character. Whoever revives DIP1039 should take care to answer questions posed in the [reviews](https://github.com/dlang/DIPs/blob/master/DIPs/other/ IP1039.md#reviews). I have some: 1. The DIP should provide a rationale as to why the feature is not allowed in function declarations. 2. The DIP does not provide enough examples; it should clearly demonstrate behavior for every situation in which a type can be used. The DIP author agrees. 3. The DIP should explain what the problem was with the first attempt and how this proposal address that problem. The DIP author disagrees. 4. The DIP should specify if arrays of character types include a terminating \0 and, if so, if it is part of the length. 5. The DIP fails to provide a rationale as to why std.array.staticArray is insufficient. 6. Better examples are needed. The DIP author agreed. 7. The DIP should use ease of reading/writing code as an argument and provide examples to that effect. The DIP author agreed. 8. The benefit gained is very minor, so the DIP should address this in relation to the difficulty of the implementation and maintenance. The DIP author agreed. My takes: 1. It makes conceptually no sense as the length is inferred from the initializer, but function parameters have none. One could argue that it does make sense for defaulted parameters, though. For template value parameters, static arrays and slices are almost equivalent anyway. It should be allowed for function return types, though. If entire types can be inferred, that should be possible. Essentially, `int[$] f()` is `auto f()` where the compiler errors if the return type isn’t convertible to a static array. 2. (Grunt work.) 3. I have no idea what the complaint even means. There is no “problem,” just a nuisance. Something that’s trivial in C is hard in D, which makes no sense. 4. I stated that, precisely, the zero terminator should be present, but not part of the static array proper. 5. It’s insufficient to interface with C APIs. Something that’s trivial (and safe) in C requires a library in D. 6. (Grunt work.) 7. (Grunt work.) 8. It is minor, no doubt about that. For quite some features of D, the benefit is small or even none technically, but the implementation isn’t gigantic either. The prime example is `=>` function definitions: Those *only* have ease of writing on their side and added zero things that couldn't easily be done without them. The `T[$]` declarations at least has the additional argument that C code can be translated to D in the core language and that literals could be stack allocated and passed to C APIs. Of course Phobos could add `staticCharArrayZ` to add the zero terminator – oh wait, it can’t because either the zero is lost on copying or part of the length. --- Other thoughts: The DIP mentions `T[$]` only in declarations and casts. It neither states that `T[$]` is a type of its own right nor does it deny it. If `T[$]` were a type, it would be a weird type, on par with `void`, probably even worse. (Hint: An `int[$]` has no values of its own, and no size. It must decay into an `int[n]` wherever it’s used. Probably many more issues.) My sense is, adding `T[$]` requires a lot of work, both specifying it and implementing it. That sets the DIP up for failure as it becomes convoluted and full of corner cases. The DIP mentioned it as a type suffix, which would, IMO, boil down to making `int[$]` a fully formed type. I can only advise anyone who thinks of rebooting the DIP: Don’t do that. The only advantage I could think of why `T[$]` should be a type is so that object.d could provide `sstring` as an alias to `immutable(char)[$]`. My best guess is that, if people who’d otherwise write `immutable(char)[$]` over and over in their code will just define `ichar = immutable(char)` and go with `ichar[$]`. That doesn’t keep us from allowing `[$]` suffixes on function return types (roughly equivalent to `auto` return types), as well as on parameter and variable declarations. For that, bake it into the syntax there: ```diff VarDeclarations: - StorageClasses? BasicType TypeSuffixes? IdentifierInitializers + StorageClasses? BasicType TypeSuffixes? StaticArraySuffixes? IdentifierInitializers Declarator: - TypeSuffixes? Identifier + TypeSuffixes? StaticArraySuffixes? Identifier FuncDeclarator: - TypeSuffixes? Identifier FuncDeclaratorSuffix + TypeSuffixes? StaticArraySuffixes? Identifier FuncDeclaratorSuffix + + StaticArraySuffixes: + [ $ + 1 ] ArraySuffixes? + [ $ ] ArraySuffixes? + + ArraySuffixes: + [ $ ] ArraySuffixes? + [ AssignExpression ] ArraySuffixes? + [] ArraySuffixes? ``` That allows, essentially, `char[$+1][][$][4]`, but not `char[$]*`. It does not bake into the grammar that `int[$+1]` isn’t possible, but it does acknowledge that `[$+1]` cannot possibly appear after another `ArraySuffix`. The reason to allow nested arrays of various kinds, but not e.g. pointers to them, is that those can be expressed in one literal: ```d char[$+1][] strings = [ "abc", "cd" ]; // as if: char[4][] strings = [ "abc", "cd" ]; // 4 == [ "abc", "cd" ].map!(x => x.length).reduce!max + 1 int[$][$] matrix = [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ] ]; // as if int[2][3] matrix = [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ] ]; ``` The DIP went into detail how it’s supposed to work with variable declarations. For function parameters, they need default values. Then, they’re basically identical to variable declarations and infer the size from the default argument. For function return types, let’s say the function return type is specified as `T[$]`. To infer the size, any `return expression;` is treated as if `return cast(T[(expression).length])expression;`. For a programmer, that would be annoying to write, but the compiler can do it. Note that what’s inside a `cast` is unevaluated, so both: the expression is evaluated exactly once; and the length must be known at compile time.
May 16