January 29, 2017
On 01/25/2017 05:47 PM, Profile Anaysis wrote:

> a 4x4 matrix and have a history of it. Just
> n 4x4 matrices but each matrix is a fixed size but there can be an
> arbitrary(dynamic) number.

I don't think using aliases is recommended yet. It can simplify things a lot:

import std.stdio;

alias Row = int[4];
alias Matrix = Row[4];
alias History = Matrix[];

Row makeRow(int value) {
    Row row;
    row = value;    // Special array syntax; all elements are now 'value'
    return row;
}

Matrix makeMatrix() {
    Matrix matrix;
    foreach (int i, ref row; matrix) {
        row = makeRow(i + 1);
    }
    return matrix;
}

void main() {
    History history;
    foreach (i; 0..3) {
        history ~= makeMatrix();
    }
    writeln(history);
}

As others have said, D's array definition is natural because unlike C's inside-out (or is that outside-in?) syntax, it follows from the alias syntax. Replacing History inside main with Matrix[], etc.:

    History history;    // is the same as:
    Matrix[] history;   // is the same as:
    Row[4][] history;   // is the same as:
    int[4][4][] history;

Ali

December 22, 2020
On Monday, 30 January 2017 at 07:33:34 UTC, Ali Çehreli wrote:
> As others have said, D's array definition is natural because unlike C's inside-out (or is that outside-in?) syntax, it follows from the alias syntax. Replacing History inside main with Matrix[], etc.:
>
>     History history;    // is the same as:
>     Matrix[] history;   // is the same as:
>     Row[4][] history;   // is the same as:
>     int[4][4][] history;
>
> Ali

Defending array-notation by giving an example of explicitly not using declared aliases makes no sense to me.
When I define 2d arrays, or index them, I think in row -> column terms (often math notation for matrix size being; 'rows x columns'), or more generally in big -> small terms, which is clear when using the consistent left->right notation, big picture first followed by detail, honestly the whole concept of encapsulation;

> History history;
> Matrix[] history;
> Row[][4] history; // collection of rows, stored in a dynamic array (top level), each containing 4 (in detail).
> int[][4][4] history; // collection of integers, stored in a dynamic array (top level), containing 4 rows (->middle level->) of 4 columns (in detail),

Of course, one can also prefer storing in columns, instead of rows, even glsl uses column-major order, just change 'Row' to 'Column' and you're set.
My argument here of course rests on outside->in thinking, which one can reverse consistently. I would be fine with that.

At heart however, this declaration design leads to inconsistency.

>    int[1][2] history; // 1 columm, 2 rows
vs
>    int last_element = history[1][0] // row 1, column 0

This makes no sense to anyone used to reading left to right, or even right to left.
Honestly, reversing reading-order when indexing & declaring is the worst idea I could imagine if consistency and readability were your goal. It doesn't make more sense because you're reading 'outward', that would mean I would have to read both left to right _and_ right to left.

The argument Jonathan M Davis gives in this regard hold no water for me.
> Like in C/C++, types are mostly read outward from the variable name in D. In both C/C++ and D,
>
> int* foo;
>
> is a pointer to an int. It's read outward from the variable name, so you get the pointer and then what it points to. Similarly,
>
> int** foo;
>
> is a pointer to a pointer to an int.

Feel free to read them the way you want, but personally, I read int* foo as 'integer pointer', which is not 'outward'. Your argument is only based on personal reading preference, but with it you remove indexing consistency & introduce boustrophedon.

You don't even always have the variable name;
> ... = new int[1][2]
vs
> ... = new int[][](2, 1)

If you were to ask someone with no prior coding experience how to access a predefined declaration, given a onedimensional explanation, I'm certain they would default to standard western reading order, no matter the presence of a variable name.

I'm surprised this thread is 3 years old by the way, sorry for that, just noticed this is how D handles multidimensional arrays (I may add a note about this to the tour suggestions).
It saddens me quite a bit, as I see it as a big design flaw and quite a turn-off, "unfortunately one that can't be fixed" as Profile Analysis put it.

> Have a nice & safe christmas!
> - Rekel, the disproportionally active forum . . . person (sorry)
December 22, 2020
Small addition, not out of jest;
If plug and play consistency given aliases is required (which seems pointless, as they exit to be used), the best solution, which would avoid indexing inconsistency given regular reading order, would be the following;

alias Row = [3]int;
[1][2][3]int history;
int last_element = history[0][1][2];
[1][2]Row history;
int last_element = history[0][1][2];
Row last_row = history[0][1];

This would be consistent, and readable.
December 21, 2020
On Tue, Dec 22, 2020 at 04:47:13AM +0000, Rekel via Digitalmars-d-learn wrote: [...]
> Defending array-notation by giving an example of explicitly not using
> declared aliases makes no sense to me.
> When I define 2d arrays, or index them, I think in row -> column terms
> (often math notation for matrix size being; 'rows x columns'), or more
> generally in big -> small terms, which is clear when using the
> consistent left->right notation, big picture first followed by detail,
> honestly the whole concept of encapsulation;

If you really want multidimensional arrays, i.e., arrays that logically range over an n-dimensional space of indices, not just arrays of arrays (which is what the array[][]... syntax gives you), I highly recommend designing your own array type using D's multidimensional array overload mechanism:

	https://dlang.org/spec/operatoroverloading.html#array-ops

This link gives only a 2D example, but it can be generalized to arbitrary dimensions.  With the proper overloads, you can do vertical/horizontal slices, subarrays, etc., all with a consistent syntax:

	arr[1, x..y]
	arr[i..j, 5]
	arr[2..3, 4..6]

See the ndslice package in the mir library for an actual such implementation.


T

-- 
Why did the mathematician reinvent the square wheel?
Because he wanted to drive smoothly over an inverted catenary road.
December 21, 2020
On 12/21/20 8:47 PM, Rekel wrote:

> On Monday, 30 January 2017 at 07:33:34 UTC, Ali Çehreli wrote:
>> As others have said, D's array definition is natural because unlike
>> C's inside-out (or is that outside-in?) syntax, it follows from the
>> alias syntax. Replacing History inside main with Matrix[], etc.:
>>
>>     History history;    // is the same as:
>>     Matrix[] history;   // is the same as:
>>     Row[4][] history;   // is the same as:
>>     int[4][4][] history;
>>
>> Ali
>
> Defending array-notation by giving an example of explicitly not using
> declared aliases makes no sense to me.

Reading 3-year younger Ali's message, I still agree with him. The aliases were not given to defend D's array syntax. They were just an additional information which I do use occasionally to simplify my code.

> When I define 2d arrays, or index them, I think in row -> column terms

Everybody have their own mental model. Because C, C++, D, three languages that I am quite familiar with, don't have 2d arrays, personally, I never felt comfortable with that mental model. I don't think C's array syntax is left-to-right or right-to-left, it's either inside-out or outside-in. As soon as I learned about D's array syntax I felt happy. Consistent...

> (often math notation for matrix size being; 'rows x columns'), or more
> generally in big -> small terms, which is clear when using the
> consistent left->right notation, big picture first followed by detail,
> honestly the whole concept of encapsulation;
>
>> History history;
>> Matrix[] history;

Ok.

>> Row[][4] history; // collection of rows, stored in a dynamic array

But that's not consistent. If array syntax is "type followed by square bracketed stuff", then your history must be

  Row[4][] history;

  Row[4] -> Four rows

Now, if we call that T, a history of those is

  T[]

Replacing T with Row[4]

  Row[4][] history;

That's what understand from consistency. What other consistent mental model is there to accept int[] as an array of ints but when it comes to an array of Row[4]s we should write Row[][4]? It's not logical nor consistent. It's a remnant of C, where the creator decided to make the definition and the use the same (or similar). Well, that did not work for me. And the creator agreed in a interview (which I am not motivated to search for now) that he might find a better syntax for C array but it was too late then.

> At heart however, this declaration design leads to inconsistency.

I disagree and I don't even understand.

>>    int[1][2] history; // 1 columm, 2 rows
> vs
>>    int last_element = history[1][0] // row 1, column 0
>
> This makes no sense to anyone used to reading left to right, or even
> right to left.

You seem to expect the language to parse history[1][0] as a whole. (?) However, history[1] is an expression by itself. There is one thing that history[1] returns and that is the second element of that array. [0] is and should be applied to whatever history[1] expression produces. Now, that's consistent.

> Honestly, reversing reading-order when indexing & declaring is the worst
> idea I could imagine if consistency and readability were your goal.

I argue the complete opposite for the same reason: consistency and readability.

> It
> doesn't make more sense because you're reading 'outward', that would
> mean I would have to read both left to right _and_ right to left.

I always read from left to right: T[]. That is the array syntax. And arr[i]; that is the array element access syntax. Everything else falls into place with those definitions.

> The argument Jonathan M Davis gives in this regard hold no water for me.

Neither to me. I am simple person. Definition: T[], use: arr[i]. Done. :)

>> Like in C/C++, types are mostly read outward from the variable name in
>> D. In both C/C++ and D,

Perhaps because I'm not a native speaker, I have never ever read any definition from right-to-left or inside out or outside in.

>> int* foo;
>>
>> is a pointer to an int.

To me, it has always been an "int pointer".

> It's read outward from the variable name, so
>> you get the pointer and then what it points to. Similarly,
>>
>> int** foo;
>>
>> is a pointer to a pointer to an int.

No: "int pointer pointer". :)

> Feel free to read them the way you want, but personally, I read int* foo
> as 'integer pointer', which is not 'outward'.

Thank you! Me too! :)

Let me try the history example:

  Row[4][] history;

Row array (of 4) array.

> just noticed this is how D handles multidimensional arrays

I think that's the source of the disagreements: D does not have multidimensional arrays. C and C++ don't have multidimensional arrays either.

> It saddens me quite a bit, as I see it as a big design flaw and quite a
> turn-off

Fully disagreed: D's array syntax makes me happy; designed right. :)

> , "unfortunately one that can't be fixed" as Profile Analysis
> put it.

Phew! :)

>> Have a nice & safe christmas!

Same to you! <3

Ali


December 22, 2020
On Tuesday, 22 December 2020 at 07:19:49 UTC, Ali Çehreli wrote:
> Let me try the history example:
>
>   Row[4][] history;
>
> Row array (of 4) array.


> Fully disagreed: D's array syntax makes me happy; designed right. :)

I think i see your point, strange how a change of words makes some things make more sense. I'll do my best to think of it like that, even if i find it intuïtive. 😅

I am curious by the way, what do you think of the [][4]Row suggestion I gave? In a way you'd have your 🍰 & could eat it too, i think ^^
(Still a strange saying to me)


December 22, 2020
On Tuesday, 22 December 2020 at 13:59:54 UTC, Rekel wrote:

>
> I am curious by the way, what do you think of the [][4]Row suggestion I gave? In a way you'd have your 🍰 & could eat it too, i think ^^
> (Still a strange saying to me)

Currently, D's variable declaration syntax is consistent and, IMO, make sense:

Type Name | Extra Tokens | SymbolName
Foo           *               a;
Foo           [4]             b;
(Foo          [4])[]          c;

[][4]Foo is completely backwards from and inconsistent with the pointer declaration syntax. We shouldn't want to intentionally introduce inconsistencies.
December 22, 2020
On 22.12.20 15:15, Mike Parker wrote:
> On Tuesday, 22 December 2020 at 13:59:54 UTC, Rekel wrote:
> 
>>
>> I am curious by the way, what do you think of the [][4]Row suggestion I gave? In a way you'd have your 🍰 & could eat it too, i think ^^
>> (Still a strange saying to me)
> 
> Currently, D's variable declaration syntax is consistent and, IMO, make sense:
> 
> Type Name | Extra Tokens | SymbolName
> Foo           *               a;
> Foo           [4]             b;
> (Foo          [4])[]          c;
> 
> [][4]Foo is completely backwards from and inconsistent with the pointer declaration syntax. We shouldn't want to intentionally introduce inconsistencies.

Flip the pointer syntax, too:

    *Foo a; /* a pointer to a Foo */
  [4]Foo b; /* an array of four Foos */
[][4]Foo c; /* a dynamic array of arrays of four Foos each */

But now we're no longer C-like, I guess.
December 22, 2020
On Tuesday, 22 December 2020 at 14:15:12 UTC, Mike Parker wrote:
> [][4]Foo is completely backwards from and inconsistent with the pointer declaration syntax. We shouldn't want to intentionally introduce inconsistencies.

The way C syntax handles pointers isn't very consistent to begin with imo.
It's strange & and * are prepended to pointer variables for example, while indexing is postpended. Leads to stuff like;
> (*array_of_pointers_to_arrays[2])[1]
vs
> array_of_pointers_to_arrays[2]*[1]

and

> (*array_of_pointers[1]).x'
vs
> 'array_of_pointers[1]*.x'

On Tuesday, 22 December 2020 at 14:35:30 UTC, ag0aep6g wrote:
> But now we're no longer C-like, I guess.

I think it'd still be quite C-like, same concepts samilar usage.
You're not using 'int foo[]' syntax anymore anyhow.
December 22, 2020
On Tuesday, 22 December 2020 at 15:24:04 UTC, Rekel wrote:
> The way C syntax handles pointers isn't very consistent to begin with imo.
> It's strange & and * are prepended to pointer variables for example, while indexing is postpended. Leads to stuff like;
>> (*array_of_pointers_to_arrays[2])[1]
> vs
>> array_of_pointers_to_arrays[2]*[1]
>
> and
>
>> (*array_of_pointers[1]).x'
> vs
>> 'array_of_pointers[1]*.x'

Don't take that as a defence of changing pointer syntax by the way, just noting I think the argument pointers and arrays should be defined using a similar syntax is not consistent when thinking about indexing & dereferencing.

Besides, I think '[4]foo* foos;' is quite clear.
"array of foo pointers" seems very natural to me.