www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - tuple-syntax

reply Dom DiSc <dominikus scherkl.de> writes:
I just viewed the D-Conf online talk about new syntax for tuples 
and had an idea I need to suggest:

One of the proposals was to use normal round brackets. But this 
would required to use something special to distinguish 
one-element tuples from from a parenthesized expression. It 
suggested a trailing comma for this case. But it has the second 
flaw that it remains unclear how to express an empty tuple 
unambiguously.

So, how about _always_ requiring a trailing comma?
This would make for a consistent syntax: (,) empty tuple, (x,) 
one-element tuple, (x,y,) two element tuple ...

Just an idea.
Mar 17
next sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Sunday, 17 March 2024 at 20:20:52 UTC, Dom DiSc wrote:
 I just viewed the D-Conf online talk about new syntax for 
 tuples and had an idea I need to suggest:

 One of the proposals was to use normal round brackets. But this 
 would required to use something special to distinguish 
 one-element tuples from from a parenthesized expression. It 
 suggested a trailing comma for this case. But it has the second 
 flaw that it remains unclear how to express an empty tuple 
 unambiguously.

 So, how about _always_ requiring a trailing comma?
 This would make for a consistent syntax: (,) empty tuple, (x,) 
 one-element tuple, (x,y,) two element tuple ...

 Just an idea.
That works but I dont like that. In my opinion that would be a case where the syntax would serve as a hint for the semantic analysis. That does not break the principle of "context-free grammar" however; just a bit odd to me. What about a property. Just `.tupleof` (one stone two birds btw...). So you put that on your one-value tuple and during sema it's clear what is it, let's say if it's an exp with parens. That works when you need an explicit type. Otherwise implicit convs from elem to tup and tup to elem would be enough. ```d auto a = (0).tupleof; // infer tuple as type static assert(isTuple, a); immutable typeof(a) b = 0; // implicit conv, from elem to tup immutable int c = b; // implicit conv, from tup to elem ```
Mar 18
parent reply Dom DiSc <dominikus scherkl.de> writes:
On Monday, 18 March 2024 at 23:52:22 UTC, Basile B. wrote:
 On Sunday, 17 March 2024 at 20:20:52 UTC, Dom DiSc wrote:
 So, how about _always_ requiring a trailing comma?
 This would make for a consistent syntax: (,) empty tuple, (x,) 
 one-element tuple, (x,y,) two element tuple ...
That works but I dont like that. In my opinion that would be a case where the syntax would serve as a hint for the semantic analysis. That does not break the principle of "context-free grammar" however; just a bit odd to me. What about a property. Just `.tupleof`
[...] ::lots of other proposals by others:: [...] Most of these were discussed in the talk, and all have cases where they become ambiguous or require new keywords / long syntax. What I like about the trailing comma is that it's short, no new keyword, and always unambiguous. Of course you could replace the comma by any other unused symbol ('EOT' or '-' are bad ideas as the one is a new keyword and the other produces some ambiguous cases, but we could use ' or : or $ or whatever), but why? Comma is an obvious choice and much less ugly than most of what I've seen else.
Mar 19
parent reply Lance Bachmeier <no spam.net> writes:
On Tuesday, 19 March 2024 at 10:04:06 UTC, Dom DiSc wrote:
 On Monday, 18 March 2024 at 23:52:22 UTC, Basile B. wrote:
 On Sunday, 17 March 2024 at 20:20:52 UTC, Dom DiSc wrote:
 So, how about _always_ requiring a trailing comma?
 This would make for a consistent syntax: (,) empty tuple, 
 (x,) one-element tuple, (x,y,) two element tuple ...
That works but I dont like that. In my opinion that would be a case where the syntax would serve as a hint for the semantic analysis. That does not break the principle of "context-free grammar" however; just a bit odd to me. What about a property. Just `.tupleof`
[...] ::lots of other proposals by others:: [...] Most of these were discussed in the talk, and all have cases where they become ambiguous or require new keywords / long syntax. What I like about the trailing comma is that it's short, no new keyword, and always unambiguous. Of course you could replace the comma by any other unused symbol ('EOT' or '-' are bad ideas as the one is a new keyword and the other produces some ambiguous cases, but we could use ' or : or $ or whatever), but why? Comma is an obvious choice and much less ugly than most of what I've seen else.
The comma is used as a separator. A trailing comma isn't used that way, so it looks weird to me. The spec says, "void has no value", so we already have a way to represent something that doesn't have a value: `(void)` and `(1, void)`. Although that's more verbose, I'm not convinced there's enough benefit to warrant the introduction of new syntax.
Mar 19
next sibling parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
On Tuesday, 19 March 2024 at 14:06:21 UTC, Lance Bachmeier wrote:
 On Tuesday, 19 March 2024 at 10:04:06 UTC, Dom DiSc wrote:
 On Monday, 18 March 2024 at 23:52:22 UTC, Basile B. wrote:
 On Sunday, 17 March 2024 at 20:20:52 UTC, Dom DiSc wrote:
 So, how about _always_ requiring a trailing comma?
 This would make for a consistent syntax: (,) empty tuple, 
 (x,) one-element tuple, (x,y,) two element tuple ...
That works but I dont like that. In my opinion that would be a case where the syntax would serve as a hint for the semantic analysis. That does not break the principle of "context-free grammar" however; just a bit odd to me. What about a property. Just `.tupleof`
[...] ::lots of other proposals by others:: [...] Most of these were discussed in the talk, and all have cases where they become ambiguous or require new keywords / long syntax. What I like about the trailing comma is that it's short, no new keyword, and always unambiguous. Of course you could replace the comma by any other unused symbol ('EOT' or '-' are bad ideas as the one is a new keyword and the other produces some ambiguous cases, but we could use ' or : or $ or whatever), but why? Comma is an obvious choice and much less ugly than most of what I've seen else.
The comma is used as a separator. A trailing comma isn't used that way, so it looks weird to me. The spec says, "void has no value", so we already have a way to represent something that doesn't have a value: `(void)` and `(1, void)`. Although that's more verbose, I'm not convinced there's enough benefit to warrant the introduction of new syntax.
void? everyone went with `_` even java.. https://openjdk.org/jeps/456 .. java.. who would have thought
Mar 19
parent Lance Bachmeier <no spam.net> writes:
On Tuesday, 19 March 2024 at 15:33:09 UTC, ryuukk_ wrote:

 The comma is used as a separator. A trailing comma isn't used 
 that way, so it looks weird to me. The spec says, "void has no 
 value", so we already have a way to represent something that 
 doesn't have a value: `(void)` and `(1, void)`. Although 
 that's more verbose, I'm not convinced there's enough benefit 
 to warrant the introduction of new syntax.
void? everyone went with `_` even java.. https://openjdk.org/jeps/456 .. java.. who would have thought
Are tuples with 0 or 1 elements going to be used enough to justify new syntax that duplicates what we already have in the language? Not just void, but also a.tupleof. `_` won't work for D, because that would be a breaking change, and I don't see any way there would be sufficient benefit to justify it. The underscore is used in C codebases (I've encountered it at least twice, including the one I'm working with right now) and I use it in foreach loops. Python uses trailing comma syntax. Assuming new syntax is desired, that would be a natural choice, even if it's ugly.
Mar 19
prev sibling parent electricface <electricface qq.com> writes:
On Tuesday, 19 March 2024 at 14:06:21 UTC, Lance Bachmeier wrote:

 The comma is used as a separator. A trailing comma isn't used 
 that way, so it looks weird to me. The spec says, "void has no 
 value", so we already have a way to represent something that 
 doesn't have a value: `(void)` and `(1, void)`. Although that's 
 more verbose, I'm not convinced there's enough benefit to 
 warrant the introduction of new syntax.
I think EOT is not a good keyword, so I use void instead of EOT.I'm reposting my existing example. ``` // tuple type and tuple literal (-) t0 = (void); (int-) t1 = (1, void); const(int-) t1 = (1, void); const(int-)* pt1 = &t1; immutable(int-) t1 = (1,void); (int-int) t2 = (1,2); (int-int-string) t2 = (1,2,"ABC"); //Why not use `Tuple!(int)` instead of `(int-)`, because `Tuple!(int)` is essentially a template, not an essential tuple type. // (-) => (int-) => (int-int) This shows that the elements of the tuple are gradually increasing // unpack auto (void) = (void); auto (a, void) = (1, void); // a = 1 // Empty tuples and single-element tuples are rarely used, so it doesn't matter if you bother to write them. // (1,) People with obsessive-compulsive disorder may think that an element is missing here, or that an element has been accidentally deleted. auto (a, b) = (1, 2); // a = 1, b = 2 // "void" appears in the first bracket and serves as a placeholder. auto (void, b) = (1, 2); // b = 2 auto (x, (y, z)) = (1, (2, 3)); // x = 1, y = 2, z = 3 auto (x, y, (z, void), (void)) = (1,2, (3, void), (void)); // x = 1, y = 2, z = 3 auto t = (1, void); // to string writeln(t); // (1) // unpack auto (x , void) = t; // x = 1 auto t2 = (1, 2); // unpack with type (int64 v1, auto v2) = t2; // v1 = 1, v2 = 2 // function define int foo(int a, int b) => a + b; int func0( (-) t ) => 0; int func1( (int-) t ) => t[0]; // with unpack int func1_u( (int-)(v1, void) ) => v1; int func2( (int-int) t ) => t[0] + t[1]; // with unpack int func2_u( (int-int)(v1, v2) ) => v1 + v2; // call function foo( 1,2 ); // ok foo( (1,2).expand ); // ok foo( (1,2) ); // error // I don't think tuples should automatically expand into multiple parameters, which would cause cognitive trouble. func0( (void) ); // ok func0(); // err func1( (1,void) ); // ok func1( 1 ); // err func1_u( (1,void)); // ok func1_u( 1 ); // err func2( (1,2) ); // ok func2( 1, 2 ); // err func2_u( (1, 2)); // ok ``` named tuple: ``` (int:a-) t1 = (a:1, void); (int:a-int:b) t2 = (a: 1, b: 2); int funcAB( (int:a-int:b)(a, b) ) => a + b; auto tab = (a: 1, b: 2); funcAB( tab ); // ok int func2(int a , int b) => a + b; func2( (b:2, a: 1).expand ); // ok func2( (b:2, a: 1) ); // err ``` with templates: `Vector!(-)` represents a vector of empty tuples. `Vector!(int-)` represents a vector of tuples with 1 int element. `Vector!(int-int)` represents a vector of tuples with 2 int elements. `(Vector!int-Vector!int)` represents a tuple of 2 vectors, where the first element's type is Vector!int and the second element's type is also the same. It should not be understood as `Vector!(int-Vector!int)`. `(Vector!(int)-Vector!(int))` same as the previous one. `(Vector!(int-int)-Vector!(int-int))` represents a tuple of 2 vectors, where the first element's type is `Vector!(int-int)` and the second element's type is also the same. lambda function: ``` auto lambda0 = ( (-) t0 ) => 0; auto lambda0 = ( (void) ) => 0; auto lambda1 = ( (int-) (a, void) ) => a; auto lambda1 = ( (a, void) ) => a; auto lambda2 = ( (int-int) (a, b) ) => a + b; or: auto lambda2 = ( (a,b) ) => a + b; // type similar to auto function( (T-T) t2) // but: auto lambdaArg2 = ( a, b ) => a + b; // type similar to auto function(T a, T b) ``` foreach: ``` auto ts = [(1,2), (3,4), (5,6)]; // with unpack: ​foreach( (int-int)(a, b); ts) { writeln(a," ", b) // 1 2\n3 4\n5 6 } ​foreach( (a, b); ts) { writeln(a," ", b) // 1 2\n3 4\n5 6 } // with unpack: ​foreach(int i, (int-int)(a, b); ts) { writeln(i," ",a," ", b) //0 1 2\n1 3 4\n2 5 6 } ​foreach( i, (a, b); ts) { writeln(i," ",a," ", b) //0 1 2\n1 3 4\n2 5 6 } ``` functions return a tuple : ``` ​(-) func0() { return (void); } ​auto func0() { return (void); } auto lambda0 = () => (void); ​(int-) func1() { return (1, void); } ​auto func1() { return (1, void); } auto lambda1 = () => (1, void); ​(int-int) func2() { return (1,2); } ​auto func2() { return (1,2); } auto lambda2 = () => (1, 2); ``` Hopefully someone will pay attention to more than just the extra trailing commas.
Mar 19
prev sibling next sibling parent reply electricface <electricface qq.com> writes:
On Sunday, 17 March 2024 at 20:20:52 UTC, Dom DiSc wrote:
 I just viewed the D-Conf online talk about new syntax for 
 tuples and had an idea I need to suggest:

 One of the proposals was to use normal round brackets. But this 
 would required to use something special to distinguish 
 one-element tuples from from a parenthesized expression. It 
 suggested a trailing comma for this case. But it has the second 
 flaw that it remains unclear how to express an empty tuple 
 unambiguously.

 So, how about _always_ requiring a trailing comma?
 This would make for a consistent syntax: (,) empty tuple, (x,) 
 one-element tuple, (x,y,) two element tuple ...

 Just an idea.
I have also reviewed the documentation for the tuple speech. Below is the tuple syntax that I have designed, which I believe is more structured and easier to distinguish: tuple type syntax EOT in tuple "()" as a keyword. ``` // tuple type and tuple literal (-) t0 = (EOT) (int-) t1 = (1, EOT) (int-int) t2 = (1,2) (int-int-string) t2 = (1,2,"ABC") // unpack auto (EOT) = (EOT); auto (a, EOT) = (1, EOT) // a = 1 auto (a, b) = (1, 2) // a = 1, b = 2 auto (x, (y, z)) = (1, (2, 3)) // x = 1, y = 2, z = 3 auto (x, y, (z, EOT), (EOT)) = (1,2, (3, EOT), (EOT)) // x = 1, y = 2, z = 3 auto t = (1, EOT) // to string writeln(t) // (1) // unpack auto (x , EOT) = t // x = 1 auto t2 = (1, 2) // unpack with type (int64 v1, auto v2) = t2 // v1 = 1, v2 = 2 // function define int foo(int a, int b) => a + b; int func0( (-) t ) => 0; int func1( (int-) t ) => t[0]; // with unpack int func1_u( (int-)(v1, EOT) ) => v1; int func2( (int-int) t ) => t[0] + t[1]; // with unpack int func2_u( (int-int)(v1, v2) ) => v1 + v2; // call function foo( 1,2 ) // ok foo( (1,2).expand ) // ok foo( (1,2) ) // error func0( (EOT) ) // ok func0() // err func1( (1,EOT) ) // ok func1( 1 ) // err func1_u( (1,EOT)) // ok func1_u( 1 ) // err func2( (1,2) ) // ok func2( 1, 2 ) // err func2_u( (1, 2)) // ok ```
Mar 18
parent reply electricface <electricface qq.com> writes:
On Tuesday, 19 March 2024 at 05:43:25 UTC, electricface wrote:
 On Sunday, 17 March 2024 at 20:20:52 UTC, Dom DiSc wrote:
 I just viewed the D-Conf online talk about new syntax for 
 tuples and had an idea I need to suggest:

 One of the proposals was to use normal round brackets. But 
 this would required to use something special to distinguish 
 one-element tuples from from a parenthesized expression. It 
 suggested a trailing comma for this case. But it has the 
 second flaw that it remains unclear how to express an empty 
 tuple unambiguously.

 So, how about _always_ requiring a trailing comma?
 This would make for a consistent syntax: (,) empty tuple, (x,) 
 one-element tuple, (x,y,) two element tuple ...

 Just an idea.
I have also reviewed the documentation for the tuple speech. Below is the tuple syntax that I have designed, which I believe is more structured and easier to distinguish:
named tuple: ``` (int:a-) t1 = (a:1, EOT); (int:a-int:b) t2 = (a: 1, b: 2); func( (int:a-int:b)(a, b) ) => a + b; auto tab = (a: 1, b: 2); func( tab ); // ok func2(int a , int b) => a + b; func2( (b:2, a: 1).expand ); // ok func2( (b:2, a: 1) ); // err ```
Mar 18
parent reply electricface <electricface qq.com> writes:
On Tuesday, 19 March 2024 at 06:10:38 UTC, electricface wrote:
 On Tuesday, 19 March 2024 at 05:43:25 UTC, electricface wrote:
 On Sunday, 17 March 2024 at 20:20:52 UTC, Dom DiSc wrote:
 I just viewed the D-Conf online talk about new syntax for 
 tuples and had an idea I need to suggest:

 One of the proposals was to use normal round brackets. But 
 this would required to use something special to distinguish 
 one-element tuples from from a parenthesized expression. It 
 suggested a trailing comma for this case. But it has the 
 second flaw that it remains unclear how to express an empty 
 tuple unambiguously.
 named tuple:
 ```
 (int:a-) t1 = (a:1, EOT);
 (int:a-int:b) t2 = (a: 1, b: 2);

 func( (int:a-int:b)(a, b) ) => a + b;
 auto tab =  (a: 1, b: 2);
 func( tab ); // ok

 func2(int a , int b) => a + b;

 func2( (b:2, a: 1).expand ); // ok
 func2( (b:2, a: 1) ); // err
 ```
with templates: `Vector!(-)` represents a vector of empty tuples. `Vector!(int-)` represents a vector of tuples with 1 int element. `Vector!(int-int)` represents a vector of tuples with 2 int elements. `(Vector!int-Vector!int)` represents a tuple of 2 vectors, where the first element's type is Vector!int and the second element's type is also the same. It should not be understood as `Vector!(int-Vector!int)`. `(Vector!(int)-Vector!(int))` same as the previous one. `(Vector!(int-int)-Vector!(int-int))` represents a tuple of 2 vectors, where the first element's type is `Vector!(int-int)` and the second element's type is also the same.
Mar 18
parent electricface <electricface qq.com> writes:
On Tuesday, 19 March 2024 at 06:59:48 UTC, electricface wrote:
 On Tuesday, 19 March 2024 at 06:10:38 UTC, electricface wrote:
 On Tuesday, 19 March 2024 at 05:43:25 UTC, electricface wrote:
 On Sunday, 17 March 2024 at 20:20:52 UTC, Dom DiSc wrote:
 I just viewed the D-Conf online talk about new syntax for 
 tuples and had an idea I need to suggest:

 One of the proposals was to use normal round brackets. But 
 this would required to use something special to distinguish 
 one-element tuples from from a parenthesized expression. It 
 suggested a trailing comma for this case. But it has the 
 second flaw that it remains unclear how to express an empty 
 tuple unambiguously.
lambda function: ``` auto lambda0 = ( (-) t0 ) => 0; auto lambda1 = ( (int-) (a, EOT) ) => a; auto lambda2 = ( (int-int) (a, b) ) => a + b; or: auto lambda2 = ( (a,b) ) => a + b; // type similar to auto function( (T-T) t2) but: auto lambdaArg2 = ( a, b ) => a + b; // type similar to auto function(T a, T b) ``` foreach: ``` auto ts = [(1,2), (3,4), (5,6)]; // with unpack: ​foreach( (int-int)(a, b); ts) { writeln(a," ", b) // 1 2\n3 4\n5 6 } ​foreach( (a, b); ts) { writeln(a," ", b) // 1 2\n3 4\n5 6 } ​foreach(int i, (int-int)(a, b); ts) { writeln(i," ",a," ", b) //0 1 2\n1 3 4\n2 5 6 } // with unpack: ​foreach( i, (a, b); ts) { writeln(i," ",a," ", b) //0 1 2\n1 3 4\n2 5 6 } ``` return a tuple : ``` ​(-) func0() { return (EOT); } ​auto func0() { return (EOT); } auto lambda0 = () => (EOT); ​(int-) func1() { return (1, EOT); } ​auto func1() { return (1, EOT); } auto lambda1 = () => (1, EOT); ​(int-int) func2() { return (1,2); } ​auto func2() { return (1,2); } auto lambda2 = () => (1, 2); ```
Mar 19
prev sibling parent Alexandru Ermicioi <alexandru.ermicioi gmail.com> writes:
On Sunday, 17 March 2024 at 20:20:52 UTC, Dom DiSc wrote:
 So, how about _always_ requiring a trailing comma?
 This would make for a consistent syntax: (,) empty tuple, (x,) 
 one-element tuple, (x,y,) two element tuple ...

 Just an idea.
I'd prefer typescript tuple syntax. It's quite clear and concise: ```typescript const x: [int, double, named: triple]; ``` I.e. In d: ```d const [int, double, named: triple] variable = [ 0, 0.0d, named: triple(1)]; const euto = [ 0, 0.0d, named: triple(1)]; static assert typeof(variable) == typeof(euto); assert euto[0] == 0; assert euto[1] == 0.0d; assert euto[2] == triple(1); assert euto.named == triple(1); ``` Best regards, Alexandru
Mar 19