digitalmars.D - tuple-syntax
- Dom DiSc (12/12) Mar 17 I just viewed the D-Conf online talk about new syntax for tuples
- Basile B. (16/28) Mar 18 That works but I dont like that. In my opinion that would be a
- Dom DiSc (14/23) Mar 19 [...]
- Lance Bachmeier (7/33) Mar 19 The comma is used as a separator. A trailing comma isn't used
- ryuukk_ (4/38) Mar 19 void? everyone went with `_`
- Lance Bachmeier (11/20) Mar 19 Are tuples with 0 or 1 elements going to be used enough to
- electricface (147/153) Mar 19 I think EOT is not a good keyword, so I use void instead of
- electricface (50/62) Mar 18 I have also reviewed the documentation for the tuple speech.
- electricface (12/31) Mar 18 named tuple:
- electricface (14/36) Mar 18 with templates:
- electricface (55/67) Mar 19 lambda function:
- Alexandru Ermicioi (20/24) Mar 19 I'd prefer typescript tuple syntax.
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
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
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:[...] ::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.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`
Mar 19
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: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.On Sunday, 17 March 2024 at 20:20:52 UTC, Dom DiSc wrote:[...] ::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.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`
Mar 19
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:void? everyone went with `_` even java.. https://openjdk.org/jeps/456 .. java.. who would have thoughtOn Monday, 18 March 2024 at 23:52:22 UTC, Basile B. 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.On Sunday, 17 March 2024 at 20:20:52 UTC, Dom DiSc wrote:[...] ::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.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`
Mar 19
On Tuesday, 19 March 2024 at 15:33:09 UTC, ryuukk_ wrote: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.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
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
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
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: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 ```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:
Mar 18
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
On Tuesday, 19 March 2024 at 06:59:48 UTC, electricface wrote:On Tuesday, 19 March 2024 at 06:10:38 UTC, electricface wrote: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); ```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.
Mar 19
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