www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - new DIP40: Template parameter deduction for constructors

reply Timothee Cour <thelastmammoth gmail.com> writes:
A proposed feature of C++14 is to introduce template parameter
deduction for constructors, see paper, mentioned here. The idea is to
deduce template parameters when calling a constructor given the
arguments given to the constructor, whenever possible. A compile error
occurs when the deduction is ambiguous. The benefits would be:
* make the code more DRY
* make boilerplate of class instantiators unnecessary in most cases
(they're all over phobos, eg: std.typecons.tuple,
std.typecons.rebindable etc)
* make D more consistent: it deduces template parameters for
functions, so why not for constructors, when this is unambiguous?
* it won't break any code.
Note, just as for deduction of normal functions, it should work with 0
or more template parameters specified (ie the first k>=0 templates may
be provided).
May 12 2013
next sibling parent reply "timotheecour" <timothee.cour2 gmail.com> writes:
The link:
http://wiki.dlang.org/DIP40
The paper links mentioned in the abstract are given in this DIP.
May 12 2013
next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Monday, 13 May 2013 at 02:39:22 UTC, timotheecour wrote:
 The link:
 http://wiki.dlang.org/DIP40
 The paper links mentioned in the abstract are given in this DIP.
Not the first time it is mentioned. Definitively a direction we want to take.
May 12 2013
prev sibling parent reply "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Monday, 13 May 2013 at 02:39:22 UTC, timotheecour wrote:
 The link:
 http://wiki.dlang.org/DIP40
 The paper links mentioned in the abstract are given in this DIP.
This DIP is lacking in detail. struct A(T1) if(!is(T1==float)) { this(T2)(T2 a, T1 b){} this()(T1 b){} this()(){} } struct A(T1) if(is(T1==float)) { this()(){} } auto a=A(1,1.0); //deduced to A!(double)(1,1.0) auto a=A(1.0); //deduced to A!(double)(1.0) auto a=A(); //error: T1 cannot be deduced. How does the compiler decide which templates to attempt to instantiate? Does it just ignore conditional compilation conditions? If so, what would it do with this? struct A(T) if (is(T==float) && is(T!=float)) { this()(T a) {} } If the conditions are ignored then it will match this uninstantiable template. If it doesn't ignore the conditions then how does it determine T ahead of time to evaluate the conditions? One possible solution could be to first ignore the conditions, match the constructor, then check that the condition is okay. This is an extension on how normal function type deduction works though, so whatever mechanisms you have in mind need to be part of the proposal.
May 13 2013
next sibling parent Timothee Cour <thelastmammoth gmail.com> writes:
Thanks for the feedback, I've clarified the deduction mechanism and
show how it falls back to normal function template deduction.
May 13 2013
prev sibling parent reply Kenji Hara <k.hara.pg gmail.com> writes:
Currently conditional compilation would stop IFTI.

template foo(T)
{
    static if (is(T == int))
        void foo(T) {}
}
void main()
{
    foo(1);   // shouldn't work
}

Same as above, DIP40 should prevent following case.

template foo(T)
{
    struct foo
    {
        static if (is(T == int))
            this(T) {}
    }
}
void main()
{
    foo(1); // also should not work
}

Kenji Hara

2013/5/14 Timothee Cour <thelastmammoth gmail.com>

 Thanks for the feedback, I've clarified the deduction mechanism and
 show how it falls back to normal function template deduction.
May 13 2013
parent reply "timotheecour" <timothee.cour2 gmail.com> writes:
On Tuesday, 14 May 2013 at 03:20:59 UTC, Kenji Hara wrote:
 Currently conditional compilation would stop IFTI.

 template foo(T)
 {
     static if (is(T == int))
         void foo(T) {}
 }
 void main()
 {
     foo(1);   // shouldn't work
 }

 Same as above, DIP40 should prevent following case.

 template foo(T)
 {
     struct foo
     {
         static if (is(T == int))
             this(T) {}
     }
 }
 void main()
 {
     foo(1); // also should not work
 }

 Kenji Hara
I think that should be consistent with the deduction mechanism proposed in the DIP: foo is struct not in scope until template foo(T) is instantiated.
May 14 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 05/14/2013 09:06 AM, timotheecour wrote:
 On Tuesday, 14 May 2013 at 03:20:59 UTC, Kenji Hara wrote:
 Currently conditional compilation would stop IFTI.

 template foo(T)
 {
     static if (is(T == int))
         void foo(T) {}
 }
 void main()
 {
     foo(1);   // shouldn't work
 }

 Same as above, DIP40 should prevent following case.

 template foo(T)
 {
     struct foo
     {
         static if (is(T == int))
             this(T) {}
     }
 }
 void main()
 {
     foo(1); // also should not work
 }

 Kenji Hara
I think that should be consistent with the deduction mechanism proposed in the DIP:
No it is not. The DIP states "find all constructors".
 foo is struct not in scope until template foo(T) is instantiated.
May 14 2013
parent reply Timothee Cour <thelastmammoth gmail.com> writes:
 I think that should be consistent with the deduction mechanism proposed
 in the DIP: foo is struct not in scope until template foo(T) is
 instantiated.
  No it is not. The DIP states "find all constructors".
it said 'Find all matching class/struct types in scope', isn't that enough or am I missing something? To clarify, I've added 3 out of scope structs named A in the example section to clarify that they won't be included in the overload set. see the ones marked '//not in scope'
May 14 2013
next sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 14 May 2013 at 07:28:57 UTC, Timothee Cour wrote:
 I think that should be consistent with the deduction mechanism 
 proposed
 in the DIP: foo is struct not in scope until template foo(T) 
 is
 instantiated.
  No it is not. The DIP states "find all constructors".
it said 'Find all matching class/struct types in scope', isn't that enough or am I missing something? To clarify, I've added 3 out of scope structs named A in the example section to clarify that they won't be included in the overload set. see the ones marked '//not in scope'
I think the best here is to specify that the rule is the same than eponymous funtion and IFTY. Otherwise we'll have 2 different specs with small difference here and there. And that sucks.
May 14 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 05/14/2013 10:04 AM, deadalnix wrote:
 On Tuesday, 14 May 2013 at 07:28:57 UTC, Timothee Cour wrote:
 I think that should be consistent with the deduction mechanism proposed
 in the DIP: foo is struct not in scope until template foo(T) is
 instantiated.
  No it is not. The DIP states "find all constructors".
it said 'Find all matching class/struct types in scope', isn't that enough or am I missing something? To clarify, I've added 3 out of scope structs named A in the example section to clarify that they won't be included in the overload set. see the ones marked '//not in scope'
I think the best here is to specify that the rule is the same than eponymous funtion and IFTY. Otherwise we'll have 2 different specs with small difference here and there. And that sucks.
There is no spec for the IFTI case. (i.e. what happens if the eponymous declaration inside the template is an overload set?)
May 14 2013
next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 14 May 2013 at 09:30:01 UTC, Timon Gehr wrote:
 There is no spec for the IFTI case. (i.e. what happens if the 
 eponymous declaration inside the template is an overload set?)
I know, but solving one problem at a time usual lead to better result than all at once.
May 14 2013
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 05/14/2013 11:30 AM, Timon Gehr wrote:
 On 05/14/2013 10:04 AM, deadalnix wrote:
 On Tuesday, 14 May 2013 at 07:28:57 UTC, Timothee Cour wrote:
 I think that should be consistent with the deduction mechanism proposed
 in the DIP: foo is struct not in scope until template foo(T) is
 instantiated.
  No it is not. The DIP states "find all constructors".
it said 'Find all matching class/struct types in scope', isn't that enough or am I missing something? To clarify, I've added 3 out of scope structs named A in the example section to clarify that they won't be included in the overload set. see the ones marked '//not in scope'
I think the best here is to specify that the rule is the same than eponymous funtion and IFTY. Otherwise we'll have 2 different specs with small difference here and there. And that sucks.
There is no spec for the IFTI case. (i.e. what happens if the eponymous declaration inside the template is an overload set?)
DMD's strategy is roughly: use the first eponymous declaration that can be found without analysing the template body for IFTI, then use the first eponymous declaration in the analyzed template body to resolve the eponymous declaration after instantiation. --- import std.stdio; template fun(T){ int fun(double arg){ return 1; } int fun(T arg){ return 0; } } void main(){ writeln(fun(2)); } // error --- import std.stdio; template fun(T){ int fun(T arg){ return 0; } int fun(double arg){ return 1; } } void main(){ writeln(fun(2)); } // ok --- This has funny implications, as the compiler may decide to resolve to a different declaration than was used for IFTI later, without doing any kind of overload resolution within the template body. --- template fun(T){ int fun(T arg){ return 0; } static if(true) int fun(double arg){ return 1; } } pragma(msg, fun(2)); // 0 --- template fun(T){ static if(true) int fun(double arg){ return 1; } int fun(T arg){ return 0; } } pragma(msg, fun(2)); // 1 --- In the second case, instantiation is performed with the second function, so T is resolved to 'int', but in the end, the 'double' overload is called with an implicit conversion.
May 14 2013
next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 14 May 2013 at 09:56:14 UTC, Timon Gehr wrote:
 DMD's strategy is roughly: use the first eponymous declaration 
 that can be found without analysing the template body for IFTI, 
 then use the first eponymous declaration in the analyzed 
 template body to resolve the eponymous declaration after 
 instantiation.

 ---
 import std.stdio;
 template fun(T){
 	int fun(double arg){ return 1; }
 	int fun(T arg){ return 0; }
 }
 void main(){ writeln(fun(2)); } // error

 ---
 import std.stdio;
 template fun(T){
 	int fun(T arg){ return 0; }
 	int fun(double arg){ return 1; }
 }
 void main(){ writeln(fun(2)); } // ok

 ---

 This has funny implications, as the compiler may decide to 
 resolve to a different declaration than was used for IFTI 
 later, without doing any kind of overload resolution within the 
 template body.

 ---
 template fun(T){
 	int fun(T arg){ return 0; }
 	static if(true) int fun(double arg){ return 1; }
 }
 pragma(msg, fun(2)); // 0
 ---
 template fun(T){
 	static if(true) int fun(double arg){ return 1; }
 	int fun(T arg){ return 0; }
 }
 pragma(msg, fun(2)); // 1
 ---

 In the second case, instantiation is performed with the second 
 function, so T is resolved to 'int', but in the end, the 
 'double' overload is called with an implicit conversion.
That creative. But completely b0rken IMO
May 14 2013
prev sibling parent reply Kenji Hara <k.hara.pg gmail.com> writes:
2013/5/14 Timon Gehr <timon.gehr gmx.ch>

 On 05/14/2013 11:30 AM, Timon Gehr wrote:

 On 05/14/2013 10:04 AM, deadalnix wrote:

 On Tuesday, 14 May 2013 at 07:28:57 UTC, Timothee Cour wrote:

 I think that should be consistent with the deduction mechanism proposed

 in the DIP: foo is struct not in scope until template foo(T) is
 instantiated.
No it is not. The DIP states "find all constructors".


 it said  'Find all matching class/struct types in scope', isn't that
 enough
 or am I missing something?

 To clarify, I've added 3 out of scope structs named A in the example
 section to clarify that they won't be included in the overload set.
 see the ones marked '//not in scope'
I think the best here is to specify that the rule is the same than eponymous funtion and IFTY. Otherwise we'll have 2 different specs with small difference here and there. And that sucks.
There is no spec for the IFTI case. (i.e. what happens if the eponymous declaration inside the template is an overload set?)
DMD's strategy is roughly: use the first eponymous declaration that can be found without analysing the template body for IFTI, then use the first eponymous declaration in the analyzed template body to resolve the eponymous declaration after instantiation. --- import std.stdio; template fun(T){ int fun(double arg){ return 1; } int fun(T arg){ return 0; } } void main(){ writeln(fun(2)); } // error --- import std.stdio; template fun(T){ int fun(T arg){ return 0; } int fun(double arg){ return 1; } } void main(){ writeln(fun(2)); } // ok --- This has funny implications, as the compiler may decide to resolve to a different declaration than was used for IFTI later, without doing any kind of overload resolution within the template body. --- template fun(T){ int fun(T arg){ return 0; } static if(true) int fun(double arg){ return 1; } } pragma(msg, fun(2)); // 0 --- template fun(T){ static if(true) int fun(double arg){ return 1; } int fun(T arg){ return 0; } } pragma(msg, fun(2)); // 1 --- In the second case, instantiation is performed with the second function, so T is resolved to 'int', but in the end, the 'double' overload is called with an implicit conversion.
Current dmd behavior is definitely a bug. Could you please file it in bugzilla? Kenji Hara
May 14 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 05/14/2013 05:08 PM, Kenji Hara wrote:
 ...

 Current dmd behavior is definitely a bug. Could you please file it in
 bugzilla?
 ...
http://d.puremagic.com/issues/show_bug.cgi?id=10083
May 14 2013
parent reply Timothee Cour <thelastmammoth gmail.com> writes:
ok Hara Kenji just fixed the bug, see
https://github.com/D-Programming-Language/dmd/pull/2041

So what's left unspecified now wrt DIP40?

My proposal was to just address case C2 below, not C1 (at least initially):

case C1:
template A(T1) {struct A{  this()(T1 a) {} }}

case C2:
struct A(T1){     this()(T1 a) {}}

Even though struct A(T1) may internally be implemented as template A(T1), I
think the most useful / less problematic conversion is just case C2.
In other words, as i said before, the constructors inside a template (as in
case C1) would note be considered as part of the overload set for this
DIP40 (at least initially).


On Tue, May 14, 2013 at 12:02 PM, Timon Gehr <timon.gehr gmx.ch> wrote:

 On 05/14/2013 05:08 PM, Kenji Hara wrote:

 ...


 Current dmd behavior is definitely a bug. Could you please file it in
 bugzilla?
 ...
http://d.puremagic.com/issues/**show_bug.cgi?id=10083<http://d.puremagic.com/issues/show_bug.cgi?id=10083>
May 16 2013
parent "deadalnix" <deadalnix gmail.com> writes:
On Thursday, 16 May 2013 at 09:12:53 UTC, Timothee Cour wrote:
 Even though struct A(T1) may internally be implemented as 
 template A(T1), I
 think the most useful / less problematic conversion is just 
 case C2.
It may not. It is, by definition.
May 16 2013
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 05/14/2013 09:28 AM, Timothee Cour wrote:
         I think that should be consistent with the deduction mechanism
         proposed
         in the DIP: foo is struct not in scope until template foo(T) is
         instantiated.

     No it is not. The DIP states "find all constructors".


 it said  'Find all matching class/struct types in scope', isn't that
 enough or am I missing something?
Apparently. The point is that it is not always possible to determine all constructors before the template is instantiated. The DIP must state when and how it works and what happens if it does not.
 To clarify, I've added 3 out of scope structs named A in the example
 section to clarify that they won't be included in the overload set.
 see the ones marked '//not in scope'
I don't get this. (Also, why is it relevant to the discussion?) The following declaration: template A(T1){ struct A{ //not in scope unless T1 is explicitly instantiated this()(T1 a) {} } } Is the same declaration, modulo constraint, as the preceding struct A(T1) if (!isNumeric!T1) { this()(T1 a) {} }
May 14 2013
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 2013-05-13 04:37, Timothee Cour wrote:
 A proposed feature of C++14 is to introduce template parameter
 deduction for constructors, see paper, mentioned here. The idea is to
 deduce template parameters when calling a constructor given the
 arguments given to the constructor, whenever possible. A compile error
 occurs when the deduction is ambiguous. The benefits would be:
 * make the code more DRY
 * make boilerplate of class instantiators unnecessary in most cases
 (they're all over phobos, eg: std.typecons.tuple,
 std.typecons.rebindable etc)
 * make D more consistent: it deduces template parameters for
 functions, so why not for constructors, when this is unambiguous?
 * it won't break any code.
 Note, just as for deduction of normal functions, it should work with 0
 or more template parameters specified (ie the first k>=0 templates may
 be provided).
I definitely want this. -- /Jacob Carlborg
May 13 2013
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 12 May 2013 22:37:43 -0400, Timothee Cour  
<thelastmammoth gmail.com> wrote:

 A proposed feature of C++14 is to introduce template parameter
 deduction for constructors, see paper, mentioned here. The idea is to
 deduce template parameters when calling a constructor given the
 arguments given to the constructor, whenever possible. A compile error
 occurs when the deduction is ambiguous. The benefits would be:
 * make the code more DRY
 * make boilerplate of class instantiators unnecessary in most cases
 (they're all over phobos, eg: std.typecons.tuple,
 std.typecons.rebindable etc)
 * make D more consistent: it deduces template parameters for
 functions, so why not for constructors, when this is unambiguous?
 * it won't break any code.
 Note, just as for deduction of normal functions, it should work with 0
 or more template parameters specified (ie the first k>=0 templates may
 be provided).
Definitely need/want this. I say it is most definitely possible given how IFTI works (it must partially instantiate the template). It should be noted that conditional compilation can stop this from working. As a first step, it should have exactly the same rules as IFTI, assuming the constructor is the eponymous IFTI function. BTW, related issue: http://d.puremagic.com/issues/show_bug.cgi?id=6082 with 4 votes so far Added a link there. -Steve
May 13 2013