## digitalmars.D - Proposal: Object/?? Destruction

• aberba (10/20) Oct 04 DIP reminds me of object destruction.
• Ilya Yaroshenko (3/4) Oct 04 ❤❤❤❤❤ I want this syntax, plz! This solves the issue how to
• SrMordred (2/16) Oct 04 +1
• John Colvin (3/24) Oct 04 People often call this "destructuring" or "unpacking" to avoid
• Timon Gehr (9/33) Oct 04 Why curly braces? Multiple function arguments are a form of built-in
• Seb (6/35) Oct 05 I think I can state the opinion of many D users here: I don't
• Timon Gehr (2/27) Oct 05 I'll create a DIP as soon as I can.
• sarn (3/7) Oct 05 All my +1s. Let's leave syntax details to people who know the D
• Steven Schveighoffer (4/9) Oct 05 I know you have an answer for this, but pardon my ignorance. Why isn't
• Timon Gehr (69/81) Oct 05 I indeed have strong opinions on how to do this correctly, as I have
• Steven Schveighoffer (31/92) Oct 06 I guess my question is more in the context of the problem at hand:
• Timon Gehr (84/195) Oct 06 That's not necessarily bad. (When is the last time you have used a
• jmh530 (13/22) Oct 06 So under your thinking, the original example should have been
• Timon Gehr (5/32) Oct 06 No, under my thinking the original example should have been what it was.
• jmh530 (4/9) Oct 06 Hmm, I hadn't realized that you can have multiple sets of
• Steven Schveighoffer (8/156) Oct 06 It is weird to me that a function with 2 parameters is the same as a
• Timon Gehr (31/56) Oct 07 If a function with 2 parameters was the same as a function that takes a
• Steven Schveighoffer (24/77) Oct 08 My questioning comes with this:
• Timon Gehr (55/153) Oct 09 Well, to me it is a bit confusing that this is puzzling to you. Why
• jmh530 (6/12) Oct 09 Singleton tuples would be necessary for any kind of recursion on
• Steven Schveighoffer (35/87) Oct 10 I understand why (int,) is different from int. What I meant was, why
• Timon Gehr (60/147) Oct 12 Because this would require a special-case rule that I had not considered...
• Q. Schroll (45/45) Oct 14 I've thought about tuples and stuff for a while. For tuples, I'll
• sarn (12/14) Oct 14 But in f([1, 2]), it's ambiguous (just by parsing) whether [1, 2]
• Q. Schroll (43/50) Oct 15 It would be a tuple if that's the best match, otherwise
• sarn (29/42) Oct 16 But have you thought through all the implications?
• Q. Schroll (50/97) Oct 29 Yes. No weirdness is being introduced that is not there already.
• Steven Schveighoffer (38/192) Oct 14 Not a problem!
• jmh530 (4/12) Oct 05 The curly bracket syntax looks straight out of DIP 32
• Timon Gehr (4/45) Oct 05 The reason why back then it seemed as if it "can't be used" is that it
• jmh530 (3/6) Oct 05 Fair enough. I only didn't have an issue with it because I had
aberba <karabutaworld gmail.com> writes:
```  Upon reading this, It triggered an idea.
On Saturday, 30 September 2017 at 16:10:44 UTC, Jonathan Marler
wrote:
https://wiki.dlang.org/DIP88

proposal rejected or is it just stale and needs a refresh?
Named parameters can be implemented in a library, however, in
my opinion they are useful enough to warrant a clean syntax
with language support.  I'd be willing to refresh the DIP so
long as I know the idea has not already been rejected.

DIP reminds me of object destruction.

/* extracts success & message from returned type. Could be tuple
or structure, etc. May even eliminate use of tuples for multiple
return
*/

auto {success, message} = callVoldermortFunction();

This is concept is used in Kotlin. JavaScript es6 takes it even
further (function parameters and arguments support object
destruction)
```
Oct 04
Ilya Yaroshenko <ilyayaroshenko gmail.com> writes:
```On Wednesday, 4 October 2017 at 10:03:56 UTC, aberba wrote:
auto {success, message} = callVoldermortFunction();

❤❤❤❤❤ I want this syntax, plz! This solves the issue how to
return multiple ref values. --Ilya
```
Oct 04
SrMordred <patric.dexheimer gmail.com> writes:
```On Wednesday, 4 October 2017 at 10:03:56 UTC, aberba wrote:
Upon reading this, It triggered an idea.
On Saturday, 30 September 2017 at 16:10:44 UTC, Jonathan
Marler wrote:
[...]

DIP reminds me of object destruction.

/* extracts success & message from returned type. Could be
tuple or structure, etc. May even eliminate use of tuples for
multiple return
*/

auto {success, message} = callVoldermortFunction();

This is concept is used in Kotlin. JavaScript es6 takes it
even further (function parameters and arguments support object
destruction)

+1
```
Oct 04
John Colvin <john.loughran.colvin gmail.com> writes:
```On Wednesday, 4 October 2017 at 10:03:56 UTC, aberba wrote:
Upon reading this, It triggered an idea.
On Saturday, 30 September 2017 at 16:10:44 UTC, Jonathan
Marler wrote:
https://wiki.dlang.org/DIP88

proposal rejected or is it just stale and needs a refresh?
Named parameters can be implemented in a library, however, in
my opinion they are useful enough to warrant a clean syntax
with language support.  I'd be willing to refresh the DIP so
long as I know the idea has not already been rejected.

DIP reminds me of object destruction.

/* extracts success & message from returned type. Could be
tuple or structure, etc. May even eliminate use of tuples for
multiple return
*/

auto {success, message} = callVoldermortFunction();

This is concept is used in Kotlin. JavaScript es6 takes it
even further (function parameters and arguments support object
destruction)

People often call this "destructuring" or "unpacking" to avoid
confusion with destructors.
```
Oct 04
bitwise <bitwise.pvt gmail.com> writes:
```On Wednesday, 4 October 2017 at 12:06:43 UTC, John Colvin wrote:
[...]

People often call this "destructuring" or "unpacking" to avoid
confusion with destructors.

Or "Structured Bindings" ;)
http://en.cppreference.com/w/cpp/language/structured_binding
```
Oct 04
=?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
```On 10/04/2017 05:06 AM, John Colvin wrote:

People often call this "destructuring"

Thanks! Now it makes sense. :)

Ali
```
Oct 04
aberba <karabutaworld gmail.com> writes:
```On Wednesday, 4 October 2017 at 12:06:43 UTC, John Colvin wrote:
On Wednesday, 4 October 2017 at 10:03:56 UTC, aberba wrote:
Upon reading this, It triggered an idea.

People often call this "destructuring" or "unpacking" to avoid
confusion with destructors.

Thats the word I was looking for. Ha ha
```
Oct 04
Timon Gehr <timon.gehr gmx.ch> writes:
```On 04.10.2017 12:03, aberba wrote:
Upon reading this, It triggered an idea.
On Saturday, 30 September 2017 at 16:10:44 UTC, Jonathan Marler wrote:
https://wiki.dlang.org/DIP88

rejected or is it just stale and needs a refresh? Named parameters
can be implemented in a library, however, in my opinion they are
useful enough to warrant a clean syntax with language support.  I'd
be willing to refresh the DIP so long as I know the idea has not

DIP reminds me of object destruction.

/* extracts success & message from returned type. Could be tuple or
structure, etc. May even eliminate use of tuples for multiple return
*/

auto {success, message} = callVoldermortFunction();

This is concept is used in Kotlin. JavaScript es6 takes it even
further (function parameters and arguments support object destruction)

Why curly braces? Multiple function arguments are a form of built-in
tuple, so the syntax should be consistent:

auto (success, message) = callVoldemortFunction();

The only unresolved question is (as using the result of the comma
operator has been deprecated already): How to write a unary tuple. My
favourite is what python does: "(3,)". This is however already accepted
as a function argument list. I think it is worth breaking though. Maybe
we should deprecate it.
```
Oct 04
Seb <seb wilzba.ch> writes:
```On Thursday, 5 October 2017 at 06:42:14 UTC, Timon Gehr wrote:
On 04.10.2017 12:03, aberba wrote:
Upon reading this, It triggered an idea.
On Saturday, 30 September 2017 at 16:10:44 UTC, Jonathan
Marler wrote:
[...]

DIP reminds me of object destruction.

/* extracts success & message from returned type. Could be
tuple or structure, etc. May even eliminate use of tuples for
multiple return
*/

auto {success, message} = callVoldermortFunction();

This is concept is used in Kotlin. JavaScript es6 takes it
even further (function parameters and arguments support object
destruction)

Why curly braces? Multiple function arguments are a form of
built-in tuple, so the syntax should be consistent:

auto (success, message) = callVoldemortFunction();

I think I can state the opinion of many D users here: I don't
mind whether it will be curly braces or round parentheses - the
important thing is that we will be able to use it in the
foreseeable future :)

The only unresolved question is (as using the result of the
comma operator has been deprecated already): How to write a
unary tuple. My favourite is what python does: "(3,)". This is
however already accepted as a function argument list. I think
it is worth breaking though. Maybe we should deprecate it.

+1
```
Oct 05
Timon Gehr <timon.gehr gmx.ch> writes:
```On 05.10.2017 17:23, Seb wrote:
auto {success, message} = callVoldermortFunction();

This is concept is used in Kotlin. JavaScript es6 takes it even
further (function parameters and arguments support object destruction)

Why curly braces? Multiple function arguments are a form of built-in
tuple, so the syntax should be consistent:

auto (success, message) = callVoldemortFunction();

I think I can state the opinion of many D users here: I don't mind
whether it will be curly braces or round parentheses - the important
thing is that we will be able to use it in the foreseeable future :)

The only unresolved question is (as using the result of the comma
operator has been deprecated already): How to write a unary tuple. My
favourite is what python does: "(3,)". This is however already
accepted as a function argument list. I think it is worth breaking
though. Maybe we should deprecate it.

+1

I'll create a DIP as soon as I can.
```
Oct 05
sarn <sarn theartofmachinery.com> writes:
```On Thursday, 5 October 2017 at 15:23:26 UTC, Seb wrote:
I think I can state the opinion of many D users here: I don't
mind whether it will be curly braces or round parentheses - the
important thing is that we will be able to use it in the
foreseeable future :)

All my +1s.  Let's leave syntax details to people who know the D
grammar inside out.
```
Oct 05
Steven Schveighoffer <schveiguy yahoo.com> writes:
```On 10/5/17 2:42 AM, Timon Gehr wrote:

The only unresolved question is (as using the result of the comma
operator has been deprecated already): How to write a unary tuple. My
favourite is what python does: "(3,)". This is however already accepted
as a function argument list. I think it is worth breaking though. Maybe
we should deprecate it.

I know you have an answer for this, but pardon my ignorance. Why isn't
(a) good enough?

-Steve
```
Oct 05
Timon Gehr <timon.gehr gmx.ch> writes:
```On 05.10.2017 17:40, Steven Schveighoffer wrote:
On 10/5/17 2:42 AM, Timon Gehr wrote:

The only unresolved question is (as using the result of the comma
operator has been deprecated already): How to write a unary tuple. My
favourite is what python does: "(3,)". This is however already
accepted as a function argument list. I think it is worth breaking
though. Maybe we should deprecate it.

I know you have an answer for this, but pardon my ignorance.

I indeed have strong opinions on how to do this correctly, as I have
given some thought to it when designing the (still quite basic) type
system of PSI: https://github.com/eth-srl/psi

The idea is to follow type theory/mathematics where the type of
functions is a binary type constructor taking domain and codomain to the
type of functions mapping values from the domain to values from the
codomain. Multiple function arguments are just the function applied to a
tuple of values.

Why isn't (a) good enough?

-Steve

typeof((a)) should be typeof(a). This is just a parenthesized
expression, as in (a+b)*c.

typeof((a,)) should be (typeof(a),).

(I'm not super keen on conflating the type of a tuple with a tuple of
types, but this has precedent in alias sequences, and I think it will be
intuitive to most D users. FWIW, Haskell also does this.)

My intention is to disentangle the concepts "function argument" and
"multiple values" as much as possible.

For example:

---
(int,string,double) foo(int a,string b,double c){
return (a,b,c);
}

(int,string) bar(int a,string b,double c){
return (a,b);
}

void main(){
auto x = foo(1,"2",3.0); // ok, typeof(x) is (int,string double)
//          ^^^^^^^^^^^
// syntax of function argument is the same as the
// syntax for a free-standing tuple:
auto y = (1,"2",3.0);

// all functions take a single argument, so you can construct
// the tuple either at the call site, or before that:
(int a,string b,double c) = foo(y); // ok
auto (x,y,z) = foo(a,b,c); // ok

// This allows natural composition of functions.
// It is like DIP 35 except better:
writeln([(1,"2",3.0), (4,"5",6.0)].map!foo.map!bar);
}
---

---
(int,) foo(int a){ return (a,); } // turn value into singleton tuple
int bar(int a,){ return a[0]; }   // turn singleton tuple into value

void main(){
foo(2,); // error: cannot convert (int,) to int
bar(2); // error: cannot convert int to (int,)
auto (x,) = foo(2); // ok, x has type int
auto y = bar(2,); // ok y has type int
auto z = foo(2); // ok, z has type (int,)
}
---

---
// The following two function signatures are equivalent (identical name
mangling):
(int,string) foo(int a,string b){
return (a,b);
}

(int,string) foo((int,string) x){
return x;
}
---

---
auto id(T)(T x){ return x; }

void main(){
auto a = id(2); // ok, a is 2.
auto b = id(1,2); // ok, b is (1,2)
auto c = id(1,); // ok, c is (1,)
}
---
```
Oct 05
Steven Schveighoffer <schveiguy yahoo.com> writes:
```On 10/5/17 3:42 PM, Timon Gehr wrote:
On 05.10.2017 17:40, Steven Schveighoffer wrote:
On 10/5/17 2:42 AM, Timon Gehr wrote:

The only unresolved question is (as using the result of the comma
operator has been deprecated already): How to write a unary tuple. My
favourite is what python does: "(3,)". This is however already
accepted as a function argument list. I think it is worth breaking
though. Maybe we should deprecate it.

I know you have an answer for this, but pardon my ignorance.

I indeed have strong opinions on how to do this correctly, as I have
given some thought to it when designing the (still quite basic) type
system of PSI: https://github.com/eth-srl/psi

The idea is to follow type theory/mathematics where the type of
functions is a binary type constructor taking domain and codomain to the
type of functions mapping values from the domain to values from the
codomain. Multiple function arguments are just the function applied to a
tuple of values.

Why isn't (a) good enough?

typeof((a)) should be typeof(a). This is just a parenthesized
expression, as in (a+b)*c.

Right, I agree.

typeof((a,)) should be (typeof(a),).

I guess my question is more in the context of the problem at hand:

int foo();

auto (a) = foo();

why can't this work?

But then of course, it shouldn't work, because int is not a tuple. So I
suppose I have answered my own question -- we need a way to specify a
tuple of one for prototype foo!

Indeed, my experience with tuples and their usage is quite limited.

Even though the syntax is straightforward and unambiguous, it looks
incorrect, like you forgot something.

I'm not an expert in language design, but would it be worth exploring
other punctuation that isn't used in the language currently to allow
better syntax? It seems like the trailing comma is to get around
ambiguity, but there would be no ambiguity if you used something other
than current punctuation to surround the tuple.

Angle brackets come to mind <a>. Also you could use a leading symbol to
change the meaning of the parentheses, like \$(a).

---
(int,) foo(int a){ return (a,); } // turn value into singleton tuple
int bar(int a,){ return a[0]; }   // turn singleton tuple into value

void main(){
foo(2,); // error: cannot convert (int,) to int
bar(2); // error: cannot convert int to (int,)
auto (x,) = foo(2); // ok, x has type int
auto y = bar(2,); // ok y has type int
auto z = foo(2); // ok, z has type (int,)
}
---

---
// The following two function signatures are equivalent (identical name
mangling):
(int,string) foo(int a,string b){
return (a,b);
}

(int,string) foo((int,string) x){
return x;
}
---

So I will ask, what is the usage of foo here?

In the first example (foo and bar), you can't call a function that takes
a tuple with a single value, and you can't call a function that takes a
value with a single tuple (BTW, this is not how AliasSeq works, you can
call functions that take a single arg with single element tuples).

In your second example, where foo takes a 2-element tuple or 2 values,
you say the name mangling is equivalent. Does that mean if I only define
the tuple version, I can call it with foo(1, "hello") and vice versa?

---
auto id(T)(T x){ return x; }

void main(){
auto a = id(2); // ok, a is 2.
auto b = id(1,2); // ok, b is (1,2)
auto c = id(1,); // ok, c is (1,)
}
---

This would mess up a TON of code. I can say for certain, a single type
argument can never be made to accept a tuple.

-Steve
```
Oct 06
Timon Gehr <timon.gehr gmx.ch> writes:
```On 06.10.2017 14:26, Steven Schveighoffer wrote:
On 10/5/17 3:42 PM, Timon Gehr wrote:
On 05.10.2017 17:40, Steven Schveighoffer wrote:
On 10/5/17 2:42 AM, Timon Gehr wrote:

The only unresolved question is (as using the result of the comma
operator has been deprecated already): How to write a unary tuple.
My favourite is what python does: "(3,)". This is however already
accepted as a function argument list. I think it is worth breaking
though. Maybe we should deprecate it.

I know you have an answer for this, but pardon my ignorance.

I indeed have strong opinions on how to do this correctly, as I have
given some thought to it when designing the (still quite basic) type
system of PSI: https://github.com/eth-srl/psi

The idea is to follow type theory/mathematics where the type of
functions is a binary type constructor taking domain and codomain to
the type of functions mapping values from the domain to values from
the codomain. Multiple function arguments are just the function
applied to a tuple of values.

Why isn't (a) good enough?

typeof((a)) should be typeof(a). This is just a parenthesized
expression, as in (a+b)*c.

Right, I agree.

typeof((a,)) should be (typeof(a),).

I guess my question is more in the context of the problem at hand:

int foo();

auto (a) = foo();

why can't this work?
...

This could be made to compile, but this is not really about tuples.

But then of course, it shouldn't work, because int is not a tuple. So I
suppose I have answered my own question -- we need a way to specify a
tuple of one for prototype foo!

Indeed, my experience with tuples and their usage is quite limited.

Even though the syntax is straightforward and unambiguous, it looks
incorrect, like you forgot something.
...

That's not necessarily bad. (When is the last time you have used a
singleton tuple?)

I'm not an expert in language design, but would it be worth exploring
other punctuation that isn't used in the language currently to allow
better syntax? It seems like the trailing comma is to get around
ambiguity,

It's the comma that indicates tupling, so there is not really ambiguity,
the expression (a) just is not a tuple. To indicate a tuple you need to
use the tupling operator ','. Trailing commas are allowed for all
tuples, but for singleton tuples they are also necessary.

but there would be no ambiguity if you used something other
than current punctuation to surround the tuple.

Angle brackets come to mind <a>.

D avoids angle brackets.

Also you could use a leading symbol to
change the meaning of the parentheses, like \$(a).
...

This is very noisy, and once you go with non-standard tuple syntax, you
can just as well use tuple(a).

---
(int,) foo(int a){ return (a,); } // turn value into singleton tuple
int bar(int a,){ return a[0]; }   // turn singleton tuple into value

void main(){
foo(2,); // error: cannot convert (int,) to int
bar(2); // error: cannot convert int to (int,)
auto (x,) = foo(2); // ok, x has type int
auto y = bar(2,); // ok y has type int
auto z = foo(2); // ok, z has type (int,)
}
---

---
// The following two function signatures are equivalent (identical
name mangling):
(int,string) foo(int a,string b){
return (a,b);
}

(int,string) foo((int,string) x){
return x;
}
---

So I will ask, what is the usage of foo here?

In the first example (foo and bar), you can't call a function that takes
a tuple with a single value, and you can't call a function that takes a
value with a single tuple (BTW, this is not how AliasSeq works, you can
call functions that take a single arg with single element tuples).
...

AliasSeq auto-expands. If you call a function with a single element
AliasSeq, it will expand to a single value and not be an AliasSeq
anymore. Built-in tuples should not auto-expand, so a singleton tuple
stays a singleton tuple (they will have an explicit .expand property).

In your second example, where foo takes a 2-element tuple or 2 values,

All functions take a single value. That value might be a tuple. (Of
course, we will continue to say that a function can take multiple
arguments, because it is convenient, but what this _means_ is that it
takes a single tuple argument.)

you say the name mangling is equivalent. Does that mean if I only define
the tuple version, I can call it with foo(1, "hello") and vice versa?

Yes. (Both options are "the tuple version".)

...

No. All functions take one argument and produce one result. (The
argument and the result may or may not be a tuple, but there is no
essential difference between the two cases.) You can match a value
against a pattern on the function call. The following are equivalent:

(int,string) foo(){
// unpack at initialization of local variables of `foo`
// pattern: (int a, string b)
// value:   (1,"2")
(int a, string b) = (1,"2");
return (a,b);
}

(int,string) foo(){
auto match(int a, string b){
return (a,b);
}
// unpack at initialization of parameters of 'match'
// pattern: (int a, string b)
// value:   (1,"2")
return match(1,"2");
}

Consider the following two different ways to achieve the same result:

(int,string) foo(){
(int a, string b) = (1,"2");
return (a,b);
}

(int,string) bar(){
(int,string) x = (1,"2");
return x;
}

We can also rewrite bar in terms of a local match function:

(int,string) bar(){
auto match((int,string) x){
return x;
}
return match(1,"2");
}

Generally, if you have a function call like:

foo(...)

You can consider the part (...) in isolation as an expression. This will

foo();   // function argument: ()
foo(1);  // function argument: (1)
foo(2,); // function argument: (2,)
foo(1,2);// function argument: (1,2)

---
auto id(T)(T x){ return x; }

void main(){
auto a = id(2); // ok, a is 2.
auto b = id(1,2); // ok, b is (1,2)
auto c = id(1,); // ok, c is (1,)
}
---

This would mess up a TON of code. I can say for certain, a single type
argument can never be made to accept a tuple.

The proposal is to make all arguments "single type arguments". The
"single type" might be a tuple. A tuple type is just a type, after all.
For two current functions where only one matches but after the change
both would match, the same one would still be selected, because it is
more specialized.

I.e., if for some reason you have:

void foo(T)(T x){
// ...
}
void foo(T,S)(T a,S b){
// ...
}
void foo(T...)(T args){
// ...
}

Then the call foo(2) will still go to the first overload and the call
foo(1,2) will still go to the second overload, while the call foo(1,2,3)
will still go to the third overload.

What relevant use case would break? (I can see the case where a
cross-module overload becomes ambiguous, but that seems a little contrived.)
```
Oct 06
jmh530 <john.michael.hall gmail.com> writes:
```On Friday, 6 October 2017 at 19:31:11 UTC, Timon Gehr wrote:
The proposal is to make all arguments "single type arguments".
The "single type" might be a tuple. A tuple type is just a
type, after all. For two current functions where only one
matches but after the change both would match, the same one
would still be selected, because it is more specialized.

[snip]
Then the call foo(2) will still go to the first overload and
the call foo(1,2) will still go to the second overload, while
the call foo(1,2,3) will still go to the third overload.

So under your thinking, the original example should have been
something like:

---
auto id(T)(T x){ return x; }

void main(){
auto a = id(2); // ok, a is 2.
auto b = id(1,2); // error, b is not single type argument
auto c = id(1,); // ok, c is 1.
auto d = id((1,2)); // ok, d is (1,2)
auto e = id((1,)); // ok, e is (1,)
}
---
```
Oct 06
Timon Gehr <timon.gehr gmx.ch> writes:
```On 06.10.2017 21:43, jmh530 wrote:
On Friday, 6 October 2017 at 19:31:11 UTC, Timon Gehr wrote:
The proposal is to make all arguments "single type arguments". The
"single type" might be a tuple. A tuple type is just a type, after
all. For two current functions where only one matches but after the
change both would match, the same one would still be selected, because
it is more specialized.

[snip]
Then the call foo(2) will still go to the first overload and the call
foo(1,2) will still go to the second overload, while the call
foo(1,2,3) will still go to the third overload.

So under your thinking, the original example should have been something
like:

---
auto id(T)(T x){ return x; }

void main(){
auto a = id(2); // ok, a is 2.
auto b = id(1,2); // error, b is not single type argument
auto c = id(1,); // ok, c is 1.
auto d = id((1,2)); // ok, d is (1,2)
auto e = id((1,)); // ok, e is (1,)
}
---

No, under my thinking the original example should have been what it was.
Enclosing an expression in an additional set of parentheses does not
change its semantics. This is true even if one set of parentheses is
part of the function call.
```
Oct 06
jmh530 <john.michael.hall gmail.com> writes:
```On Friday, 6 October 2017 at 19:51:39 UTC, Timon Gehr wrote:
No, under my thinking the original example should have been
what it was.
Enclosing an expression in an additional set of parentheses
does not change its semantics. This is true even if one set of
parentheses is part of the function call.

Hmm, I hadn't realized that you can have multiple sets of
parentheses without error. I was assuming you would treat the
second set as a tuple.
```
Oct 06
Steven Schveighoffer <schveiguy yahoo.com> writes:
```On 10/6/17 3:31 PM, Timon Gehr wrote:
On 06.10.2017 14:26, Steven Schveighoffer wrote:
On 10/5/17 3:42 PM, Timon Gehr wrote:
On 05.10.2017 17:40, Steven Schveighoffer wrote:
On 10/5/17 2:42 AM, Timon Gehr wrote:

The only unresolved question is (as using the result of the comma
operator has been deprecated already): How to write a unary tuple.
My favourite is what python does: "(3,)". This is however already
accepted as a function argument list. I think it is worth breaking
though. Maybe we should deprecate it.

I know you have an answer for this, but pardon my ignorance.

I indeed have strong opinions on how to do this correctly, as I have
given some thought to it when designing the (still quite basic) type
system of PSI: https://github.com/eth-srl/psi

The idea is to follow type theory/mathematics where the type of
functions is a binary type constructor taking domain and codomain to
the type of functions mapping values from the domain to values from
the codomain. Multiple function arguments are just the function
applied to a tuple of values.

Why isn't (a) good enough?

typeof((a)) should be typeof(a). This is just a parenthesized
expression, as in (a+b)*c.

Right, I agree.

typeof((a,)) should be (typeof(a),).

I guess my question is more in the context of the problem at hand:

int foo();

auto (a) = foo();

why can't this work?
...

This could be made to compile, but this is not really about tuples.

But then of course, it shouldn't work, because int is not a tuple. So
I suppose I have answered my own question -- we need a way to specify
a tuple of one for prototype foo!

Indeed, my experience with tuples and their usage is quite limited.

Even though the syntax is straightforward and unambiguous, it looks
incorrect, like you forgot something.
...

That's not necessarily bad. (When is the last time you have used a
singleton tuple?)

I'm not an expert in language design, but would it be worth exploring
other punctuation that isn't used in the language currently to allow
better syntax? It seems like the trailing comma is to get around
ambiguity,

It's the comma that indicates tupling, so there is not really ambiguity,
the expression (a) just is not a tuple. To indicate a tuple you need to
use the tupling operator ','. Trailing commas are allowed for all
tuples, but for singleton tuples they are also necessary.

but there would be no ambiguity if you used something other than
current punctuation to surround the tuple.

Angle brackets come to mind <a>.

D avoids angle brackets.

Also you could use a leading symbol to change the meaning of the
parentheses, like \$(a).
...

This is very noisy, and once you go with non-standard tuple syntax, you
can just as well use tuple(a).

---
(int,) foo(int a){ return (a,); } // turn value into singleton tuple
int bar(int a,){ return a[0]; }   // turn singleton tuple into value

void main(){
foo(2,); // error: cannot convert (int,) to int
bar(2); // error: cannot convert int to (int,)
auto (x,) = foo(2); // ok, x has type int
auto y = bar(2,); // ok y has type int
auto z = foo(2); // ok, z has type (int,)
}
---

---
// The following two function signatures are equivalent (identical
name mangling):
(int,string) foo(int a,string b){
return (a,b);
}

(int,string) foo((int,string) x){
return x;
}
---

So I will ask, what is the usage of foo here?

In the first example (foo and bar), you can't call a function that
takes a tuple with a single value, and you can't call a function that
takes a value with a single tuple (BTW, this is not how AliasSeq
works, you can call functions that take a single arg with single
element tuples).
...

AliasSeq auto-expands. If you call a function with a single element
AliasSeq, it will expand to a single value and not be an AliasSeq
anymore. Built-in tuples should not auto-expand, so a singleton tuple
stays a singleton tuple (they will have an explicit .expand property).

In your second example, where foo takes a 2-element tuple or 2 values,

All functions take a single value. That value might be a tuple. (Of
course, we will continue to say that a function can take multiple
arguments, because it is convenient, but what this _means_ is that it
takes a single tuple argument.)

you say the name mangling is equivalent. Does that mean if I only
define the tuple version, I can call it with foo(1, "hello") and vice
versa?

Yes. (Both options are "the tuple version".)

...

No. All functions take one argument and produce one result. (The
argument and the result may or may not be a tuple, but there is no
essential difference between the two cases.) You can match a value
against a pattern on the function call.

It is weird to me that a function with 2 parameters is the same as a
function that takes a 2-element tuple, but a function with one parameter
is not the same as a function that takes a 1-element tuple. That is
where I feel it's a contradiction.

This would mess up a TON of code. I can say for certain, a single type
argument can never be made to accept a tuple.

The proposal is to make all arguments "single type arguments". The
"single type" might be a tuple. A tuple type is just a type, after all.
For two current functions where only one matches but after the change
both would match, the same one would still be selected, because it is
more specialized.

Right, but cases where T is expected to match to exactly one type will
now match with multiple types. It messes up is(typeof(...)) checks.

-Steve
```
Oct 06
Timon Gehr <timon.gehr gmx.ch> writes:
```On 06.10.2017 23:34, Steven Schveighoffer wrote:

No. All functions take one argument and produce one result. (The
argument and the result may or may not be a tuple, but there is no
essential difference between the two cases.) You can match a value
against a pattern on the function call.

It is weird to me that a function with 2 parameters is the same as a
function that takes a 2-element tuple, but a function with one parameter
is not the same as a function that takes a 1-element tuple. That is
where I feel it's a contradiction.
...

If a function with 2 parameters was the same as a function that takes a
2-element tuple, and a function with one parameter that is a 2-element
tuple is the same as a function that takes a 1-element tuple, then a
function that takes a 2-element tuple is the same as a function that
takes a 1-element tuple. So I think the opposite is the case.

// those two are the same
void foo(int a,string b); // match two-element tuple
void foo((int,string) x); // take two-element tuple w/o matching

// those two are the same
void bar(int a,);   // match one-element tuple
void bar((int,) x); // take one-element tuple w/o matching

This is like:

(int a,string b)=(1,"2"); // match
// vs
(int,string) x=(1,"2"); // w/o matching

and

(int a,)=(1,); // match
// vs
(int,) x=(1,); // w/o matching

In case this is not convincing to you: Why does your reasoning apply to
arguments but not return values? Why should arguments not behave the
same as return values? If it does actually apply to return values: what
special syntax would you propose for functions that "return multiple
values"? Is it really reasonable to not use tuples for that?

This would mess up a TON of code. I can say for certain, a single
type argument can never be made to accept a tuple.

The proposal is to make all arguments "single type arguments". The
"single type" might be a tuple. A tuple type is just a type, after
all. For two current functions where only one matches but after the
change both would match, the same one would still be selected, because
it is more specialized.

Right, but cases where T is expected to match to exactly one type will
now match with multiple types. It messes up is(typeof(...)) checks.

-Steve

All new language features can be detected using is(typeof(...)) this is
usually ignored for language evolution. We'd need to check how much code
relies on this specific case not compiling.

We can also think about adding a "light" version of tuple support, that
just supports unpacking for library-defined tuple types and nothing
else, but I'd prefer to have proper tuples.
```
Oct 07
Steven Schveighoffer <schveiguy yahoo.com> writes:
```On 10/7/17 8:56 PM, Timon Gehr wrote:
On 06.10.2017 23:34, Steven Schveighoffer wrote:

No. All functions take one argument and produce one result. (The
argument and the result may or may not be a tuple, but there is no
essential difference between the two cases.) You can match a value
against a pattern on the function call.

It is weird to me that a function with 2 parameters is the same as a
function that takes a 2-element tuple, but a function with one
parameter is not the same as a function that takes a 1-element tuple.
That is where I feel it's a contradiction.
...

If a function with 2 parameters was the same as a function that takes a
2-element tuple, and a function with one parameter that is a 2-element
tuple is the same as a function that takes a 1-element tuple, then a
function that takes a 2-element tuple is the same as a function that
takes a 1-element tuple. So I think the opposite is the case.

// those two are the same
void foo(int a,string b); // match two-element tuple
void foo((int,string) x); // take two-element tuple w/o matching

// those two are the same
void bar(int a,);   // match one-element tuple
void bar((int,) x); // take one-element tuple w/o matching

This is like:

(int a,string b)=(1,"2"); // match
// vs
(int,string) x=(1,"2"); // w/o matching

and

(int a,)=(1,); // match
// vs
(int,) x=(1,); // w/o matching

My questioning comes with this:

void bar(int a);
void bar((int,) x);

To me, it is confusing or at least puzzling that these two aren't the same.

The first is like a "regular" function that doesn't take a tuple.

The second is a new "tuplized" function that takes a tuple. both take
one parameter (one version via the regular argument syntax, one via a
tuplized syntax). Why is it not the same? Clearly a tuple of 1 can bind
to a single value, just like a tuple of 2 can bind to 2 values.

Currently, I can call this:

foo(T...)(T t) if (T.length == 1) // function that takes a single
element tuple

like this:

foo(1);

Why is this disallowed in your tuple scheme?

In case this is not convincing to you: Why does your reasoning apply to
arguments but not return values? Why should arguments not behave the
same as return values? If it does actually apply to return values: what
special syntax would you propose for functions that "return multiple
values"? Is it really reasonable to not use tuples for that?

I don't understand the question. I would think single value tuples and
single values would be pretty much interchangeable. It's up to the user
of the value whether he wants to look at it as a tuple (which has length
and must be indexed) vs. a single value.

Right, but cases where T is expected to match to exactly one type will
now match with multiple types. It messes up is(typeof(...)) checks.

All new language features can be detected using is(typeof(...)) this is
usually ignored for language evolution. We'd need to check how much code
relies on this specific case not compiling.

I definitely don't have an answer off hand, but I wouldn't be surprised
if this broke at least some code in phobos.

We can also think about adding a "light" version of tuple support, that
just supports unpacking for library-defined tuple types and nothing
else, but I'd prefer to have proper tuples.

This flew over my head :)

-Steve
```
Oct 08
Timon Gehr <timon.gehr gmx.ch> writes:
```On 09.10.2017 01:20, Steven Schveighoffer wrote:
On 10/7/17 8:56 PM, Timon Gehr wrote:
On 06.10.2017 23:34, Steven Schveighoffer wrote:

No. All functions take one argument and produce one result. (The
argument and the result may or may not be a tuple, but there is no
essential difference between the two cases.) You can match a value
against a pattern on the function call.

It is weird to me that a function with 2 parameters is the same as a
function that takes a 2-element tuple, but a function with one
parameter is not the same as a function that takes a 1-element tuple.
That is where I feel it's a contradiction.
...

If a function with 2 parameters was the same as a function that takes
a 2-element tuple, and a function with one parameter that is a
2-element tuple is the same as a function that takes a 1-element
tuple, then a function that takes a 2-element tuple is the same as a
function that takes a 1-element tuple. So I think the opposite is the
case.

// those two are the same
void foo(int a,string b); // match two-element tuple
void foo((int,string) x); // take two-element tuple w/o matching

// those two are the same
void bar(int a,);   // match one-element tuple
void bar((int,) x); // take one-element tuple w/o matching

This is like:

(int a,string b)=(1,"2"); // match
// vs
(int,string) x=(1,"2"); // w/o matching

and

(int a,)=(1,); // match
// vs
(int,) x=(1,); // w/o matching

My questioning comes with this:

void bar(int a);
void bar((int,) x);

To me, it is confusing or at least puzzling that these two aren't the same.
...

Well, to me it is a bit confusing that this is puzzling to you. Why
should int be the same as (int,)? It does not make sense to index an
integer, but (int,) can be indexed with 0 to get an integer.

I believe your difficulty is rather with the notion that what before was
a function that takes a single value is no longer analogous to what
before was a function that takes multiple values. The analogy breaks
because now they are handled precisely the same way, rather than just
analogously. Furthermore, some existing syntax slightly changes meaning:
The prior syntax for declaring multiple arguments is now a pattern that
matches against a single tuple argument.

The new design is more orthogonal and in effect more useful, because
functions no longer need to care about and interfere with the concept of
"multiple values".

The first is like a "regular" function that doesn't take a tuple.

The second is a new "tuplized" function that takes a tuple. both take
one parameter (one version via the regular argument syntax, one via a
tuplized syntax).

One takes an argument that is an integer. The other takes an argument
that is a tuple containing a single integer.

Why is it not the same?

One takes an int while the other takes an (int,).

Clearly a tuple of 1 can bind
to a single value, just like a tuple of 2 can bind to 2 values.
...

But it is precisely what is happening. However, not every value is a
singleton tuple just by virtue of not being a tuple of multiple values.

Currently, I can call this:

foo(T...)(T t) if (T.length == 1) // function that takes a single
element tuple

like this:

foo(1);

Why is this disallowed in your tuple scheme?
...

I take this to mean, why does the following code not compile:

void foo(T)(T t) if(T.length == 1) { ... }

foo(1);

This is because T is matched to int, which does not have length.

A few more examples:

void foo(int x);       // foo takes an int
void foo(int x,);      // foo takes an (int,) and matches it against
(int x,) in order to extract the value x
void foo(int x,int y); // foo takes an (int,int) and matches it against
(int x,int y) in order to extract the values x and y.

In case this is not convincing to you: Why does your reasoning apply
to arguments but not return values? Why should arguments not behave
the same as return values? If it does actually apply to return values:
what special syntax would you propose for functions that "return
multiple values"? Is it really reasonable to not use tuples for that?

I don't understand the question.

I'm was trying to figure out what is causing the confusion. I was trying
to appeal to symmetry to get past what seemed to be your notion that
there is a significant difference between multiple values and a single
tuple: If you have a function that needs to return multiple values, you
return a single tuple. If you have a function that needs to take
multiple values, you take a single tuple. With tuples, it is sufficient
to return a single value, and it is also sufficient to take a single
value as the argument.

I would think single value tuples and
single values would be pretty much interchangeable.

Well, no. Otherwise 2[0] would be allowed and equal to 2. And then, what
would [2][0] be? [2] or 2?

It's up to the user
of the value whether he wants to look at it as a tuple (which has length
and must be indexed) vs. a single value.
...

A singleton tuple is like a box that contains a single value. You need
to open the box to get at the value. Opening the box is achieved by
matching the tuple against a tuple pattern. This is the same regardless
of the length of the tuple.

Singleton tuples might seem pointless, and some languages do not support
such single-element tuples, but as we want to support slicing, they
should probably exist. (Also, we might want to create a tuple from an
AliasSeq, which can be achieved by dropping it into a single-element
tuple and letting it auto-expand.)

Right, but cases where T is expected to match to exactly one type
will now match with multiple types. It messes up is(typeof(...)) checks.

All new language features can be detected using is(typeof(...)) this
is usually ignored for language evolution. We'd need to check how much
code relies on this specific case not compiling.

I definitely don't have an answer off hand, but I wouldn't be surprised
if this broke at least some code in phobos.
...

I guess we'll need to try.

We can also think about adding a "light" version of tuple support,
that just supports unpacking for library-defined tuple types and
nothing else, but I'd prefer to have proper tuples.

This flew over my head :)
...

If we cannot have proper tuples, having some syntactic sugar for tuple
unpacking during variable declaration may still be useful:

import std.typecons;

auto (x,y) = tuple(1,"2");
(int x,string y) = tuple(1,"2");

This is syntactically forward-compatible.
```
Oct 09
jmh530 <john.michael.hall gmail.com> writes:
```On Monday, 9 October 2017 at 15:22:35 UTC, Timon Gehr wrote:
Singleton tuples might seem pointless, and some languages do
not support such single-element tuples, but as we want to
support slicing, they should probably exist. (Also, we might
want to create a tuple from an AliasSeq, which can be achieved
by dropping it into a single-element tuple and letting it
auto-expand.)

Singleton tuples would be necessary for any kind of recursion on
tuples.

The way mir.functional.RefTuple works is similar to your AliasSeq
tuple. This is because it uses anonymous names for the fields.
std.typecons.Tuple assumes field name/Type pairs.
```
Oct 09
Steven Schveighoffer <schveiguy yahoo.com> writes:
```On 10/9/17 11:22 AM, Timon Gehr wrote:
On 09.10.2017 01:20, Steven Schveighoffer wrote:
My questioning comes with this:

void bar(int a);
void bar((int,) x);

To me, it is confusing or at least puzzling that these two aren't the
same.
...

Well, to me it is a bit confusing that this is puzzling to you. Why
should int be the same as (int,)? It does not make sense to index an
integer, but (int,) can be indexed with 0 to get an integer.

I understand why (int,) is different from int. What I meant was, why
can't I *call* a function that takes a single int tuple with a single
int value?

It shouldn't matter to the caller whether you plan to fiddle with your
parameter via tuple syntax or directly with a value.

Again, I go back to the 2-parameter version. I can call it with 2
values, or a tuple of 2 values. It makes no difference to the callee how
I call it, as long as I put 2 values on the stack.

I don't see why it should be different for a single parameter function.

To put it another way, in your scheme, what is the benefit to
overloading a single value function call with a function call that takes
a single element tuple? When would this be useful?

Currently, I can call this:

foo(T...)(T t) if (T.length == 1) // function that takes a single
element tuple

like this:

foo(1);

Why is this disallowed in your tuple scheme?
...

I take this to mean, why does the following code not compile:

void foo(T)(T t) if(T.length == 1) { ... }

foo(1);

Nope, I meant my original. A "tuple" as D currently uses it, can have
exactly one element, and I can call that function with exactly one
value. I don't have to call it as:

AliasSeq!(int) v;
v[0] = 1;
foo(v);

Which is analogous to your requirements (obviously, D is missing the
syntax for tuple literals, which is why it's complicated).

Note that if foo is:

foo(int x);

I can still call it with v. I don't see why we can't keep these kinds of
allowances.

I would think single value tuples and single values would be pretty
much interchangeable.

Well, no. Otherwise 2[0] would be allowed and equal to 2. And then, what
would [2][0] be? [2] or 2?

Not interchangeable in terms of usage, but interchangeable in terms of

What I would have expected is for foo(int) and foo((int,)) to be
equivalent mangling (like the bar(int, int) and bar((int, int)) are
equivalent), and for the caller to be able to call those functions with
either a single value or a singleton tuple.

Inside the function, of course, they are treated differently as the
callee decides whether to unpack the tuple or not via the parameters.

We can also think about adding a "light" version of tuple support,
that just supports unpacking for library-defined tuple types and
nothing else, but I'd prefer to have proper tuples.

This flew over my head :)
...

If we cannot have proper tuples, having some syntactic sugar for tuple
unpacking during variable declaration may still be useful:

import std.typecons;

auto (x,y) = tuple(1,"2");
(int x,string y) = tuple(1,"2");

This is syntactically forward-compatible.

OK, this makes sense, yes.

-Steve
```
Oct 10
Timon Gehr <timon.gehr gmx.ch> writes:
```On 10.10.2017 17:05, Steven Schveighoffer wrote:
On 10/9/17 11:22 AM, Timon Gehr wrote:
On 09.10.2017 01:20, Steven Schveighoffer wrote:
My questioning comes with this:

void bar(int a);
void bar((int,) x);

To me, it is confusing or at least puzzling that these two aren't the
same.
...

Well, to me it is a bit confusing that this is puzzling to you. Why
should int be the same as (int,)? It does not make sense to index an
integer, but (int,) can be indexed with 0 to get an integer.

I understand why (int,) is different from int. What I meant was, why
can't I *call* a function that takes a single int tuple with a single
int value?
...

Because this would require a special-case rule that I had not considered
so far. This is up to discussion though.

to my expected behaviour?", and not: "Why do your rules not allow
this?", but it seems I have misinterpreted your question. Sorry for the
confusion! :)

It shouldn't matter to the caller whether you plan to fiddle with your
parameter via tuple syntax or directly with a value.
...

I see. I think what you propose does make sense, as it might smoothen
out the interaction with other D language features such as variadics and

Again, I go back to the 2-parameter version. I can call it with 2
values, or a tuple of 2 values.

With the caveat that those two cases are actually identical, yes.

auto x = (1,"2"); // construct value
f(x); // now call f with value

and

f(1,"2"); // construct tuple and call f with the resulting value

It is like:

auto x = [1,2];
f(x);

and

f([1,2]);

Except that redundant parentheses are optional:
f(1,"2") is exactly equivalent to f((1,"2")) after parsing.

f. f(((1,"2"))) is also the same expression for the same reason.

Note that this does not compile:

void f(int a,int b,int c){}
f(1,(2,3));

The reason is that I tried to call f with an argument of type
(int,(int,int)), while it expected an argument of type (int,int,int).

It makes no difference to the callee how
I call it, as long as I put 2 values on the stack.
...

Well, I think it should maybe not be possible to conflate e.g. (int,int)
and ((int,),(int,)).

I don't see why it should be different for a single parameter function.
...

I think you are making a strong point here.

To put it another way, in your scheme, what is the benefit to
overloading a single value function call with a function call that takes
a single element tuple? When would this be useful?
...

I agree that this is actually not useful.

Note that this means the following code will be accepted also:

void foo(int x,int y,int z){}

foo((1,2,3),);

Currently, I can call this:

foo(T...)(T t) if (T.length == 1) // function that takes a single
element tuple

like this:

foo(1);

Why is this disallowed in your tuple scheme?
...

I take this to mean, why does the following code not compile:

void foo(T)(T t) if(T.length == 1) { ... }

foo(1);

Nope, I meant my original. A "tuple" as D currently uses it, can have
exactly one element, and I can call that function with exactly one
value. I don't have to call it as:

AliasSeq!(int) v;
v[0] = 1;
foo(v);

Which is analogous to your requirements (obviously, D is missing the
syntax for tuple literals, which is why it's complicated).

Note that if foo is:

foo(int x);

I can still call it with v. I don't see why we can't keep these kinds of
allowances.
...

I see. Well, we can't keep them to the extent AliasSeq has them.
AliasSeq always auto-expands. Auto-expansion for tuples can become a
problem, especially in generic code, because it forgets structure
information. For example:

void printElements(T)(T[] arr){
foreach(x;enumerate(a)){
print("at index ",x[0]," we have ",x[1]);
}
}

auto a = [(1,2),(3,4),(5,6)];
printElements(a);

With auto-expansion, this prints:
at index 0, we have 1
at index 1, we have 3
at index 2, we have 5

However, it is quite clear from the definition of printElements that the
programmer wanted it to print:

at index 0, we have (1,2)
at index 1, we have (3,4)
at index 2, we have (5,6)

AliasSeq does not have this specific problem, because it cannot be put
into an array without expanding.

I would think single value tuples and single values would be pretty
much interchangeable.

Well, no. Otherwise 2[0] would be allowed and equal to 2. And then,
what would [2][0] be? [2] or 2?

Not interchangeable in terms of usage, but interchangeable in terms of

What I would have expected is for foo(int) and foo((int,)) to be
equivalent mangling (like the bar(int, int) and bar((int, int)) are
equivalent), and for the caller to be able to call those functions with
either a single value or a singleton tuple.

Inside the function, of course, they are treated differently as the
callee decides whether to unpack the tuple or not via the parameters.

This makes sense, and I think your proposal improves the design.
```
Oct 12
Q. Schroll <qs.il.paperinik gmail.com> writes:
```I've thought about tuples and stuff for a while. For tuples, I'll
use [brackets]. Reasons follow.

Homogeneous tuples are repetitions of some single type. We have
them today in form of static arrays. We could allow
"inhomogeneous arrays" and call them tuples. T[n] is then an
alias for [T, T, .., T] with n repititions. In place of a type
[T, S] means Tuple!(T, S) and in place of an Object [t, s] means
tuple(t, s). Note that D's grammar allows disambiguate types and
objects by syntax.

A tuple implicitly converts to some other, if the pointwise types
do. Bracket literals constitute a separate type that exists in
the compiler only. We have that already to make
int[2] a = [ 1, 2 ];
not allocate on the heap, but
int[]  a = [ 1, 2 ];
does. So at first, [ 1, 2.0 ] is of type [int, double]. If you
assign it to a double[2], because int -> double, the conversion
is no problem. The thing that changes, is when you ask for
typeof([ 1, 2.0 ]) directly. Of course,
auto tup = [ 1, 2.0 ];
will homogenize the tuple to double[] similar to how it does
today. Declaration-decomposition can be done as
auto [a, b] = f(x);
(non-exlusive) or
[auto a, auto b] = f(x);
The first one is shorter, the latter one let's you do
[int a, auto b] = f(x);
So auto [x1, .. xn] is just a shorthand for [auto x1, .. auto xn].
Assignment-decomposition is the same with the types/auto missing.
Swap can be done with
[a, b] = [b, a];
From the type system, if a tuple literal has only lvalues inside,
it is an lvalue, too.
Note that there must be some way to handle side effects
correctly. The problem is already known from normal assignment.

1-tuples are in a natural way included. int[1] is today different
from int. When we have first-class tuples in D, we should not
distinguish static arrays from homogeneous tuples.

Therefore, and because of brackets, you can distinguish f(1, 2)
from f([1, 2]). I find the syntax (1,) for 1-tuples weird.
Parenthesis are used only for operator precedence and function
calls. They should not be used for tuples -- the 1-tuple case and
f(1, 2) vs f((1, 2)) prove that. Parenthesis are a tool of
syntax, not semantics. You can never omit brackets in D.

Maybe you can use some of that as input for your DIP.
```
Oct 14
sarn <sarn theartofmachinery.com> writes:
```On Saturday, 14 October 2017 at 22:20:46 UTC, Q. Schroll wrote:
Therefore, and because of brackets, you can distinguish f(1, 2)
from f([1, 2]).

But in f([1, 2]), it's ambiguous (just by parsing) whether [1, 2]
is a tuple literal or a dynamic array literal.

You'd need to use a prefix or something to the bracket syntax.
E.g., \$[1,2].  The dollar sign is just an example, but it might
work because currently \$ only seems to be used in slice syntax
and in inline assembly.  I don't think tuple literals are needed
in inline assembly, and I think the slice syntax might be okay
because (unlike C) D doesn't allow the borked 0[arr] style of
indexing, so expressions like arr[0..\$[x][y]] are unambiguous.
(I.e., \$[x][y] is the yth element of a tuple containing x, and
not the yth element of the \$th element of x as an array).
```
Oct 14
Q. Schroll <qs.il.paperinik gmail.com> writes:
```On Saturday, 14 October 2017 at 23:20:26 UTC, sarn wrote:
On Saturday, 14 October 2017 at 22:20:46 UTC, Q. Schroll wrote:
Therefore, and because of brackets, you can distinguish f(1,
2) from f([1, 2]).

But in f([1, 2]), it's ambiguous (just by parsing) whether [1,
2] is a tuple literal or a dynamic array literal.

It would be a tuple if that's the best match, otherwise
conversion to int[] is tried. Even today, [1, 2] is ambiguous: Is
it a static or a dynamic array of int? Is it of type int[2] or
int[]? The spec says, it depends what you do with it! We can
progress that and enlarge the int[2] version to [int, int] -- a
special case of a 2-tuple. It remains the same: If [1, 2] can be
used as a dynamic array, it will be. If not, the compiler tries a
static array. With tuples, it would try a tuple. If f has an
overload taking int[] or something similar, it will treat [1, 2]
as a dynamic array with homogeneus types. If the objects are not
compatible, an error occurs like "tuple [..contents..] cannot be
implicitly converted to T[]". Else, if it has an overload for a
compatible (length, implicit conversion) tuple, that will be
taken. Consider
void f(int[2] v) { } // (1)
void f(int[ ] v) { } // (2)
Here, f([1, 2]) calls (1) as it is the better match. Yet with
auto x = [1, 2];
f(x) calls (2) because of strict typing. So while [1, 2] is of
type int[2] or [int, int] as a tuple, typeof([1, 2]) will still
yield int[]. You cannot ask the one and only correct type of
[]-literals as they have more than one type. Even if the values
are incompatible like [1, "a"], asking typeof([1, "a"]) will
result in an error, because in typeof deduction, []-literals must
result in dynamic arrays. This holds for auto, because auto has
the same rules.
auto tup = [1, "a"];
must fail. You'd need
[auto, auto] tup = [1, "a"];
or maybe some shorthand syntax that lowers to this.

You'd need to use a prefix or something to the bracket syntax.
[snip]

I just argued, you don't!

The reason there is no such prefix and not even a function in
Phobos, is it is a trivial task to make one.
T[n] s(T, size_t n)(T[n] elem ...) { return elem; }
static assert(is(typeof(s(1, 2, 3)) == int[3]));
static assert(is(typeof([1, 2, 3].s) == int[3]));
auto x = s(1, 2, 3);
static assert(is(typeof(x) == int[3]));
auto x = s(1, 2.0, 3);
static assert(is(typeof(x) == double[3]));
Try it yourself. It works fine. Instead of s one would use t or
tuple to allow incompatible types.
```
Oct 15
sarn <sarn theartofmachinery.com> writes:
```On Sunday, 15 October 2017 at 15:19:21 UTC, Q. Schroll wrote:
On Saturday, 14 October 2017 at 23:20:26 UTC, sarn wrote:
On Saturday, 14 October 2017 at 22:20:46 UTC, Q. Schroll wrote:
Therefore, and because of brackets, you can distinguish f(1,
2) from f([1, 2]).

But in f([1, 2]), it's ambiguous (just by parsing) whether [1,
2] is a tuple literal or a dynamic array literal.

It would be a tuple if that's the best match, otherwise
conversion to int[] is tried.
...
You'd need to use a prefix or something to the bracket syntax.
[snip]

I just argued, you don't!

But have you thought through all the implications?

Take this code:

void main(string[] args)
{
import std.stdio : writeln;
writeln([1, 3.14]);
}

As you're probably 100% aware, this is totally valid D code
today.  [1, 3.14] becomes a double[] because 1 gets converted to
a double.  If this kind of behaviour changes, code will break, so
you'll need a bunch of exceptions to the "it would be a tuple if
that's the best match" rule.  Also, for the same backwards
compatibility reasons, it would be impractical in most cases to
functions that currently accept slices or arrays, but presumably
new functions would be meant to take advantage of the new syntax
(else there wouldn't be much point creating a new syntax).

So, a literal like [1, 3.14] would basically be a tuple, but
would be converted to double[] in a bunch of special cases for
historical reasons.

If you're not sure if this is really a problem, take a look at
the confusion caused by the magic in {} syntax:

To be totally honest, I still don't see what's wrong with just
creating a new bracket syntax, instead of adding more magic to []
(or () for that matter).
```
Oct 16
Q. Schroll <qs.il.paperinik gmail.com> writes:
```On Monday, 16 October 2017 at 23:29:46 UTC, sarn wrote:
On Sunday, 15 October 2017 at 15:19:21 UTC, Q. Schroll wrote:
On Saturday, 14 October 2017 at 23:20:26 UTC, sarn wrote:
On Saturday, 14 October 2017 at 22:20:46 UTC, Q. Schroll
wrote:
Therefore, and because of brackets, you can distinguish f(1,
2) from f([1, 2]).

But in f([1, 2]), it's ambiguous (just by parsing) whether
[1, 2] is a tuple literal or a dynamic array literal.

It would be a tuple if that's the best match, otherwise
conversion to int[] is tried.
...
You'd need to use a prefix or something to the bracket syntax.
[snip]

I just argued, you don't!

But have you thought through all the implications?

Yes. No weirdness is being introduced that is not there already.
Maybe I have overseen something; I will not give you or anyone
else a guarantee for the solution to work perfectly. I've thought
through the case very long.
An open question is allowing partly const/immutable/shared (cis)
tuples. As for now, I didn't care. Even c/i/s-homogeneus tuples
(the tuple is c/i/s as a whole or not) would be a win in my
opinion. One rarely needs tuples with one component immutable but
the other one mutable. This is what a named struct is for. On the
other hand, I don't know of any issues having a to partly c/i/s
std.typecons.Tuple.

Take this code:

void main(string[] args)
{
import std.stdio : writeln;
writeln([1, 3.14]);
}

As you're probably 100% aware, this is totally valid D code
today.  [1, 3.14] becomes a double[] because 1 gets converted
to a double.

Right conclusion with insufficient explanation. [1, 3.14] is a
static array in the first place. It occupies a fully inferred
template parameter position. I don't know the implementation, but
every time I tested, it behaves as if typeof(expr) is being used
after the bang to set the template argument manually (even for
Voldemort types etc. where typeof is sometimes impossible due to
missing frame pointers). typeof returns "dynamic array of T" for
array literals. This is all the weirdness going on here. It is
present today and would remain present if you interpret [1, 3.14]
as a tuple.

If this kind of behaviour changes, code will break, so you'll
need a bunch of exceptions to the "it would be a tuple if
that's the best match" rule.

The only exception is typeof and (therefore, I don't know...)
template inference.

Also, for the same backwards compatibility reasons, it would be
existing standard library functions that currently accept
slices or arrays, but presumably new functions would be meant
to take advantage of the new syntax (else there wouldn't be
much point creating a new syntax).

You don't have to as long as you don't want to support tuples
explicitly; otherwise you have to. If you have a void f(int,
double), you cannot plug in [1, 3.14]. You can use some expand to
do it. You wouldn't want to either. If you have something
*explicitly typed* as a tuple, e.g.
[int, double] tup = [1, 3.14];
you can make the call f(tup) because auto expansion does its job.
This is the use case. If you have void f([int, double]), you can
plug in tuple literals.
If you use a tuple literal for a function call, the compiler will
search for explicit matches for tuples. If it cannot find any,
conversion to a dynamic array happens.

So, a literal like [1, 3.14] would basically be a tuple, but
would be converted to double[] in a bunch of special cases for
historical reasons.

Yes. It would be converted in almost all cases -- the same with
static arrays -- because the best match doesn't occur very often
and typeof never returns static arrays or tuples for literals.

If you're not sure if this is really a problem, take a look at
the confusion caused by the magic in {} syntax:

This is completely unrelated. Concerning the issues people have
with (..) => { .. }, I've filed an enhancement request to
deprecate it in that specific case:
https://issues.dlang.org/show_bug.cgi?id=17951

To be totally honest, I still don't see what's wrong with just
[] (or () for that matter).

It's not adding any magic to [] that isn't there already. The
other proposals are adding magic to (). Even some mathematicians
use chevrons (angle brackets) for tuples as they see parentheses
as indicators of precedence. I'd vote against angle brackets, see
C++ templates for reasons. Logicians and haskellers even don't
need parentheses for function calls.

Could I convince you?
```
Oct 29
Steven Schveighoffer <schveiguy yahoo.com> writes:
```Sorry for waiting so long to respond, I had to think about this a lot...

On 10/12/17 3:05 PM, Timon Gehr wrote:
On 10.10.2017 17:05, Steven Schveighoffer wrote:
On 10/9/17 11:22 AM, Timon Gehr wrote:
On 09.10.2017 01:20, Steven Schveighoffer wrote:
My questioning comes with this:

void bar(int a);
void bar((int,) x);

To me, it is confusing or at least puzzling that these two aren't
the same.
...

Well, to me it is a bit confusing that this is puzzling to you. Why
should int be the same as (int,)? It does not make sense to index an
integer, but (int,) can be indexed with 0 to get an integer.

I understand why (int,) is different from int. What I meant was, why
can't I *call* a function that takes a single int tuple with a single
int value?
...

Because this would require a special-case rule that I had not considered
so far. This is up to discussion though.

to my expected behaviour?", and not: "Why do your rules not allow
this?", but it seems I have misinterpreted your question. Sorry for the
confusion! :)

Not a problem!

Again, I go back to the 2-parameter version. I can call it with 2
values, or a tuple of 2 values.

With the caveat that those two cases are actually identical, yes.

auto x = (1,"2"); // construct value
f(x); // now call f with value

and

f(1,"2"); // construct tuple and call f with the resulting value

It is like:

auto x = [1,2];
f(x);

and

f([1,2]);

Except that redundant parentheses are optional:
f(1,"2") is exactly equivalent to f((1,"2")) after parsing.

f. f(((1,"2"))) is also the same expression for the same reason.

Note that this does not compile:

void f(int a,int b,int c){}
f(1,(2,3));

Right, there is a structural difference here. The 2 and 3 being tied
together is a piece of information that is lost or different than is
expected. I get that, and agree with it.

The reason is that I tried to call f with an argument of type
(int,(int,int)), while it expected an argument of type (int,int,int).

It makes no difference to the callee how I call it, as long as I put 2
values on the stack.
...

Well, I think it should maybe not be possible to conflate e.g. (int,int)
and ((int,),(int,)).

This, I'm not 100% sure on. In my mind, the difference between a value
and a tuple of one element is trivial and negligible. I think they
should be implicitly convertible between each other. Perhaps they would
still be mangled differently, but you could still call both with the
different forms. Sort of like const(int) and int have different
semantics, but I can call a function with one or the other.

In that sense, perhaps foo(int) and foo((int,)) are different manglings,
but you can still call either form with either form.

I don't see why it should be different for a single parameter function.
...

I think you are making a strong point here.

To put it another way, in your scheme, what is the benefit to
takes a single element tuple? When would this be useful?
...

I agree that this is actually not useful.

Note that this means the following code will be accepted also:

void foo(int x,int y,int z){}

foo((1,2,3),);

Yes, that seems reasonable to me.

Currently, I can call this:

foo(T...)(T t) if (T.length == 1) // function that takes a single
element tuple

like this:

foo(1);

Why is this disallowed in your tuple scheme?
...

I take this to mean, why does the following code not compile:

void foo(T)(T t) if(T.length == 1) { ... }

foo(1);

Nope, I meant my original. A "tuple" as D currently uses it, can have
exactly one element, and I can call that function with exactly one
value. I don't have to call it as:

AliasSeq!(int) v;
v[0] = 1;
foo(v);

Which is analogous to your requirements (obviously, D is missing the
syntax for tuple literals, which is why it's complicated).

Note that if foo is:

foo(int x);

I can still call it with v. I don't see why we can't keep these kinds
of allowances.
...

I see. Well, we can't keep them to the extent AliasSeq has them.
AliasSeq always auto-expands. Auto-expansion for tuples can become a
problem, especially in generic code, because it forgets structure
information. For example:

void printElements(T)(T[] arr){
foreach(x;enumerate(a)){
print("at index ",x[0]," we have ",x[1]);
}
}

auto a = [(1,2),(3,4),(5,6)];
printElements(a);

With auto-expansion, this prints:
at index 0, we have 1
at index 1, we have 3
at index 2, we have 5

However, it is quite clear from the definition of printElements that the
programmer wanted it to print:

at index 0, we have (1,2)
at index 1, we have (3,4)
at index 2, we have (5,6)
AliasSeq does not have this specific problem, because it cannot be put
into an array without expanding.

Right, I was specifically focusing on what I understood -- the existing
mechanism of "singleton tuple" in D.

But really, I think implicit expanding or tupling of values when needed
would fix all the problems I had, fits into the current expectations of
D users, and allows your scheme to work.

So to recap my thoughts:

1. I think T and (T,) can be different types, probably should be mangled
differently, but implicitly cast to one another, similar to T and
const(T) when T is a value type.
2. I'm OK with fun(T,T) and fun((T,T)) being equivalent.
3. I'm still not in love with (T,) as a singleton tuple type, it looks
like you accidentally put in a trailing comma. But I don't have a better
syntax to suggest.
4. I'm still convinced that having a template type match a tuple when
multiple parameters are passed as in:

foo(T)(T t) {...}

foo(1, 2); // OK, T = (int, int)

is not going to go well with existing code.

On point 4, as a compromise, would it be possible to opt-in to such
things via a new syntax? For example something like:

foo((T,...))(T t) // T is a tuple of 0 or more items.

One other thing, what does an empty tuple look like? Is it (,)?

-Steve
```
Oct 14
jmh530 <john.michael.hall gmail.com> writes:
```On Thursday, 5 October 2017 at 06:42:14 UTC, Timon Gehr wrote:
Why curly braces? Multiple function arguments are a form of
built-in tuple, so the syntax should be consistent:

auto (success, message) = callVoldemortFunction();

The only unresolved question is (as using the result of the
comma operator has been deprecated already): How to write a
unary tuple. My favourite is what python does: "(3,)". This is
however already accepted as a function argument list. I think
it is worth breaking though. Maybe we should deprecate it.

The curly bracket syntax looks straight out of DIP 32

https://wiki.dlang.org/DIP32
auto {x, y} = {1, "hi"};.
```
Oct 05
Timon Gehr <timon.gehr gmx.ch> writes:
```On 05.10.2017 17:48, jmh530 wrote:
On Thursday, 5 October 2017 at 06:42:14 UTC, Timon Gehr wrote:
Why curly braces? Multiple function arguments are a form of built-in
tuple, so the syntax should be consistent:

auto (success, message) = callVoldemortFunction();

The only unresolved question is (as using the result of the comma
operator has been deprecated already): How to write a unary tuple. My
favourite is what python does: "(3,)". This is however already
accepted as a function argument list. I think it is worth breaking
though. Maybe we should deprecate it.

The curly bracket syntax looks straight out of DIP 32

https://wiki.dlang.org/DIP32
auto {x, y} = {1, "hi"};.

There are many good ideas in DIP32, including this one:

Basic () syntax, perhaps the cleanest, but can't be used:

----
import std.stdio, std.algorithm, std.container, std.array;

auto encode(T)(Group!("a == b", T[]) sf) {
auto heap = sf.map!((c, f) => (f, [(c, "")])).array.heapify!q{b < a};

while (heap.length > 1) {
auto (lof, loa) = heap.front;  heap.removeFront;
auto (hif, hia) = heap.front;  heap.removeFront;
foreach ((_, ref e); loa) e = '0' ~ e;
foreach ((_, ref e); hia) e = '1' ~ e;
heap.insert((lof + hif, loa ~ hia));
}
return heap.front[1].schwartzSort!((c, e) => (e.length, c));
}

void main() {
auto s = "this is an example for huffman encoding"d;
foreach ((c, e); s.dup.sort().release.group.encode)
writefln("'%s'  %s", c, e);
}
---

The reason why back then it seemed as if it "can't be used" is that it
was taken by the comma operator. This is no longer the case.
```
Oct 05
jmh530 <john.michael.hall gmail.com> writes:
```On Thursday, 5 October 2017 at 19:55:15 UTC, Timon Gehr wrote:
The reason why back then it seemed as if it "can't be used" is
that it was taken by the comma operator. This is no longer the
case.

Fair enough. I only didn't have an issue with it because I had
recalled it when reading the previous DIP.
```
Oct 05