Proposal: Object/?? Destruction

Steven Schveighoffer schveiguy at yahoo.com
Sun Oct 15 01:07:01 UTC 2017


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.
> 
> I interpreted your question to be: "Why do your proposed rules not lead 
> 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.
> 
> The second expression just adds an additional pair of parentheses around 
> 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 
>> 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),);
> 
> Does this match your expectation?

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


More information about the Digitalmars-d mailing list