www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 2779] New: alias this + tuple expansion on function call doesn't work

reply d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779

           Summary: alias this + tuple expansion on function call doesn't
                    work
           Product: D
           Version: 2.027
          Platform: PC
        OS/Version: Windows
            Status: NEW
          Keywords: rejects-valid
          Severity: normal
          Priority: P2
         Component: DMD
        AssignedTo: bugzilla digitalmars.com
        ReportedBy: dsimcha yahoo.com


struct Tuple(T...) {
    T data;
    alias data this;
}

void doStuff(uint a, float b) {}

void main() {
    Tuple!(uint, float) foo;
    doStuff(foo[0], foo[1]); // Works.
    doStuff(foo.tupleof);  // Works.
    doStuff(foo.data);  // Works.

    // Error: function test.doStuff (uint a, float b)
    // does not match parameter types (Tuple!(uint,float))
    // Error: cannot implicitly convert expression (foo) of type
    // Tuple!(uint,float) to uint|
    // Error: expected 2 function arguments, not 1
    doStuff(foo);
}


-- 
Apr 01 2009
next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779


Kenji Hara <k.hara.pg gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |patch
                 CC|                            |k.hara.pg gmail.com



Patch:
https://github.com/D-Programming-Language/dmd/pull/74

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jul 14 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779


Andrei Alexandrescu <andrei metalanguage.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |andrei metalanguage.com



07:34:34 PDT ---
I think it's best to leave things as they are and close this. Automatic
expansion is a bad thing. Consider for example:

struct Tuple(T...) {
    T data;
    alias data this;
}

void doStuff(Tuple!(uint, float) t) {}
void doStuff(uint a, float b) {}

In this case we need to introduce a new rule that says the tuple should NOT be
expanded if there's a matching function, or a new rule that makes the call
ambiguous.

Things are good as they are. If you want to expand, say t.expand. If you want
to pass the tuple as a unit, say t.

Kenji, please advise if you agree, and if so let's close this.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jul 22 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779





 I think it's best to leave things as they are and close this. Automatic
 expansion is a bad thing. Consider for example:
 
 struct Tuple(T...) {
     T data;
     alias data this;
 }
 
 void doStuff(Tuple!(uint, float) t) {}
 void doStuff(uint a, float b) {}

 In this case we need to introduce a new rule that says the tuple should NOT be
 expanded if there's a matching function, or a new rule that makes the call
 ambiguous.
This case does not introduce ambiguous. Alias this lookup will run only when original type matching fails. So 1st doStuff will match. We can treat tuple expansion on function arguments based on alias this rule, not any new rule. My patch also work like it. https://github.com/D-Programming-Language/dmd/pull/74/files#L7R144 func6_22 call with Tup!(int, string) object matches 2nd overload version. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 22 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779




08:10:42 PDT ---
Fair point. I still fear this feature would cause more confusion than
convenience, particularly because function overloading rules are already so
complex. As an example, changing the type of one element in a tuple becomes a
surprising maintenance liability.

In brief, I think we shouldn't change the language such that writing f(x) may
or may not expand automatically and depending on certain conditions into f(x1,
x2, ..., xn). I actually very strongly believe it would be a net liability.

I'll let Walter decide on this.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jul 22 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779




My patch's expansion strategy is simple, They are 'Head first' and 'No back
tracking'.

Example:
----
auto tup = tuple(tuple(10, "str"), tuple(3.14, [1,2]);
void func(int, string, double, int[]);

func(tup);  // function call with tuple argument
// func with Tuple!(Tuple!(int, string), Tuple!(double, int[]))

Resolving overloads fails
At this point, current D makes this expression error.
But my patch continue to resolve overloads and rewrite expression like follows.

auto __t = tup;  // do not evaluate tuple many times
func(__t[0], __t[1]);
// func with (Tuple!(int, string), Tuple!(double, int[]))

Resolving overloads fails, so go next expansion.

auto __t = tup;
func(__t[0][0], __t[0][1], __t[1]); // do not expand second tuple at this point
// func with (int, string, Tuple!(double, int[]))

Resolving overloads fails, so go next expansion.

auto __t = tup;
func(__t[0][0], __t[0][1], __t[1][0], __t[1][1]);
// func with (int, string, double, int[])

Overload matches, so expansion is succeeded.
(If we can't expand tuple anymore and resolving overloads fails, calling func
becomes error.)
----

Only one head tuple is expanded on one step ('Head first'),
and already expanded tuples are never re-packed ('No back tracking').

I think this rule is not so complex.
It is not make current valid calling invalid.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jul 22 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779




09:10:47 PDT ---
I understand your point, but also please consider how e.g. changing "double" to
"float" in your second Tuple may cause a completely different path to be
followed.

For new code, the proposed feature looks interesting. For existing code, I
think it only makes things difficult for everyone. And for what reason? Let's
not forget what this feature brings us: the ability to not type ".expand".
TODAY if I write:

fun(tup);

I just KNOW that func will be infoked with ONE argument, and if I write:

fun(tup.expand);

I also KNOW that fun will be DEFINITELY be invoked with EXACTLY as many
arguments as elements are in tup. That all is great.

The notion of the compiler taking the initiative of automatically expanding a
tuple partially or totally transitively in attempt to find a match seems a
complication very difficult to justify. I understand how the rules are simple
and logical, but I don't think the result is.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jul 22 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779




I understand your argue. You want to distinguish one argument and many
arguments explicitly.

But... it is inconsistent to me.

My patch also support tuple expanding on initializer, foreach aggregator, and
foreach range front.

On Initializer:
----
TypeTuple!(int, double) f = tuple(10, 2.2);
assert(f[0] == 10);
assert(f[1] == 2.2);
----
Sample is (int, double) vs Tuple!(int, double).
It is similar to  void func(int, double) vs tup (typed as Tuple!(int, double)).
If we allow automatically expansion on initializer, it will be obscure why not
allow expansion on function arguments.
Or, we should write like follows?
----
TypeTuple!(int, double) f = tuple(10, 2.2).expand;
----

On Foreach aggregator:
----
auto tup = tuple(10, 2.2, "str");
foreach (e; tup)
{
    static if(is(typeof(e)==int)) assert(e == 10);
    static if(is(typeof(e)==double)) assert(e == 2.2);
    static if(is(typeof(e)==string)) assert(e == "str");
}
----
We can write tup.expand instead of tup. This is in line with your claim.

On Foreach range front:
----
auto r = zip(sequence!"n"(), "hello world")
foreach (i, c; r)
{
   if (i == 0) assert(c == 'h');
   if (i == 1) assert(c == 'e');
   ...
}
----
According to your claim, we should write r.front.expand, but it can't.
In this case, automatically expansion is NEED.
Is automatically expansion support depending on locaiton?


Totally, disallowing auto-expansion only on function arguments is inconsistent
to me.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jul 22 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779




10:02:50 PDT ---
I agree with your consistency argument. But there are many other angles. We
definitely want to avoid automatic expansion in function arguments, where I
think any consistency advantage is pale compared to the numerous liabilities. I
can think of a huge amount of examples, e.g. multiple-arguments functions:

fun(t1, t2, t3, t4); // some or all are tuples

There are a combinatorial amount of possible expansions. By the consistency
argument, you should try all and use what works if there's only one match. But
also that would cause the head of the reader to explode. We simply can't allow
such a thing in the language.

Here's what I suggest we do to move things forward: change your pull request to
only improve "foreach (e; tup)" which is a net step forward. That request has a
much better chance of getting approval.

I'm not sure about the "foreach (i, c; r)" case because we can't implement ref
arguments properly, so probably we need to discuss that some more.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jul 22 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779


Jacob Carlborg <doob me.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |doob me.com



I don't see why you would want to treat alias this with a tuple different from
a "regular" alias this. Why do we have alias this at all?

struct Foo
{
    int value;
}

void bar (int i );

Foo foo;

bar(foo.value);

We have alias this JUST so we don't have to write ".value" in the above
example.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jul 22 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779




10:20:55 PDT ---
Jacob: yes, and that's a great thing. What's not so great is the interaction
between tuples and matching arguments to functions, as I explained. I didn't
argue that saving ".whatever" is not worthy IN GENERAL.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jul 22 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779




Andrei: I don't see how it could be much different when matching one argument
and several.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jul 22 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779




11:11:48 PDT ---

 Andrei: I don't see how it could be much different when matching one argument
 and several.
I explained in my argument about this: fun(t1, t2, t3, t4); How do you suggest we handle that? -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 22 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779






 Andrei: I don't see how it could be much different when matching one argument
 and several.
I explained in my argument about this: fun(t1, t2, t3, t4); How do you suggest we handle that?
Depends on how the function is declared and what's passed to the function. If the original type matches (i.e. the tuple) then don't expand the tuple. If the tuple doesn't match then try to expand the tuple. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 22 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779




11:24:19 PDT ---



 Andrei: I don't see how it could be much different when matching one argument
 and several.
I explained in my argument about this: fun(t1, t2, t3, t4); How do you suggest we handle that?
Depends on how the function is declared and what's passed to the function. If the original type matches (i.e. the tuple) then don't expand the tuple. If the tuple doesn't match then try to expand the tuple.
fun could be overloaded and might use variadics and template constraints. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 22 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779




Say we have this:

struct Foo
{
    int value;
    alias value this;
}

If an "instance" of Foo is passed to a function taking variadic arguments then
it's received as Foo and not int, correct? Why would an alias this with a tuple
behave any different. In that case you would have to manually expand it.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Jul 22 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779




---

 If an "instance" of Foo is passed to a function taking variadic arguments then
 it's received as Foo and not int, correct? 
It is correct. Variadic functions take arguments itself, not expanded. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 22 2011
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779




11:44:32 PDT ---

 Say we have this:
 
 struct Foo
 {
     int value;
     alias value this;
 }
 
 If an "instance" of Foo is passed to a function taking variadic arguments then
 it's received as Foo and not int, correct? Why would an alias this with a tuple
 behave any different. In that case you would have to manually expand it.
This is not only about variadic arguments, sorry for being confusing. It is about the combinatorial explosion when overloading rules meet automatic expansion. Again, please consider this example: fun(t1, t2, t3, t4); and consider: * fun may have an arbitrary number of overloads * Some or all of t1 through t4 may be tuples, some also including tuples of tuples The fact that there exists an algorithm to find an expansion (albeit in potentially a long time) is little solace when we consider the complexity inflicted on the human reader. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 22 2011
prev sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=2779




 This is not only about variadic arguments, sorry for being confusing. It is
 about the combinatorial explosion when overloading rules meet automatic
 expansion. Again, please consider this example:
 
 fun(t1, t2, t3, t4);
 
 and consider:
 
 * fun may have an arbitrary number of overloads
 * Some or all of t1 through t4 may be tuples, some also including tuples of
 tuples
 
 The fact that there exists an algorithm to find an expansion (albeit in
 potentially a long time) is little solace when we consider the complexity
 inflicted on the human reader.
Ok, I can see now that it can become quite complicated. Specially with nested tuples. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Jul 22 2011