www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - More templated type argument inference

reply "bearophile" <bearophileHUGS lycos.com> writes:
Currently this code is not supported:

void foo(T)(T x, T function(T) f) {}
void main() {
     foo(1, (int a) => a * a); // OK
     foo(1, a => a * a);       // Error
}


With the latest alpha compiler it gives:

test.d(4): Error: template test.foo does not match any function 
template declaration. Candidates are:
test.d(1):        test.foo(T)(T x, T function(T) f)
test.d(4): Error: template test.foo(T)(T x, T function(T) f) 
cannot deduce template function from argument types !()(int,void)


Do you think it's a good idea to allow it?

Bye,
bearophile
Dec 06 2012
next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 6 December 2012 at 13:04:05 UTC, bearophile wrote:
 Currently this code is not supported:

 void foo(T)(T x, T function(T) f) {}
 void main() {
     foo(1, (int a) => a * a); // OK
     foo(1, a => a * a);       // Error
 }


 With the latest alpha compiler it gives:

 test.d(4): Error: template test.foo does not match any function 
 template declaration. Candidates are:
 test.d(1):        test.foo(T)(T x, T function(T) f)
 test.d(4): Error: template test.foo(T)(T x, T function(T) f) 
 cannot deduce template function from argument types 
 !()(int,void)


 Do you think it's a good idea to allow it?

 Bye,
 bearophile

The second is template lambda and templates have void type. What you suggest here?
Dec 06 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Maxim Fomin:

 The second is template lambda and templates have void type. 
 What you suggest here?

In D template functions become functions if they are passed where the D compiler can infer their full type. This is correct code: void foo(int function(int) f) {} void main() { foo(a => a * a); } Bye, bearophile
Dec 06 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Thursday, 6 December 2012 at 13:04:05 UTC, bearophile wrote:
 Currently this code is not supported:

 void foo(T)(T x, T function(T) f) {}
 void main() {
     foo(1, (int a) => a * a); // OK
     foo(1, a => a * a);       // Error
 }

I forgot there is int here, so T in lambda should be deduced as int as well.
 With the latest alpha compiler it gives:

 test.d(4): Error: template test.foo does not match any function 
 template declaration. Candidates are:
 test.d(1):        test.foo(T)(T x, T function(T) f)
 test.d(4): Error: template test.foo(T)(T x, T function(T) f) 
 cannot deduce template function from argument types 
 !()(int,void)


 Do you think it's a good idea to allow it?

 Bye,
 bearophile

Does makes sense.
Dec 06 2012
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
--047d7b6da40c36ab6b04d0315395
Content-Type: text/plain; charset=UTF-8

Doesn't that mean you're asking the compiler to have a full unification
engine? Your example is simple, but the general case is more complicated:

void foo(T,U,V)(Tuple!(T,U) tup, V function(U) fun)
{ ... }

void main()
{
    foo(tuple(1,"abc"), (a) => 1.34); // so, I can deduce V to be double

    foo(tuple(1,"abc"), (int i) => i+); // error, U unified to string and
int!
}

--047d7b6da40c36ab6b04d0315395
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

<div>Doesn&#39;t that mean you&#39;re asking the compiler to=C2=A0have a fu=
ll unification engine? Your example is simple, but the general case is more=
 complicated:</div>
<div>=C2=A0</div>
<div>void foo(T,U,V)(Tuple!(T,U) tup, V function(U) fun)</div>
<div>{ ... }</div>
<div>=C2=A0</div>
<div>void main()</div>
<div>{</div>
<div>=C2=A0=C2=A0=C2=A0 foo(tuple(1,&quot;abc&quot;), (a) =3D&gt; 1.34); //=
 so, I can deduce V to be double</div>
<div>=C2=A0</div>
<div>=C2=A0=C2=A0=C2=A0 foo(tuple(1,&quot;abc&quot;), (int i) =3D&gt; i+); =
// error, U unified to string and int!</div>
<div>}</div>
<div>=C2=A0</div>
<div>=C2=A0</div>

--047d7b6da40c36ab6b04d0315395--
Dec 06 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Philippe Sigaud:

 Doesn't that mean you're asking the compiler to have a full 
 unification
 engine? Your example is simple, but the general case is more 
 complicated:

I think the general case doesn't require a whole program type inference, just a local one. But maybe it's too much complex to do it in D :-) I don't know. Bye, bearophile
Dec 06 2012
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 12/06/2012 02:04 PM, bearophile wrote:
 Currently this code is not supported:

 void foo(T)(T x, T function(T) f) {}
 void main() {
      foo(1, (int a) => a * a); // OK
      foo(1, a => a * a);       // Error
 }


 With the latest alpha compiler it gives:

 test.d(4): Error: template test.foo does not match any function template
 declaration. Candidates are:
 test.d(1):        test.foo(T)(T x, T function(T) f)
 test.d(4): Error: template test.foo(T)(T x, T function(T) f) cannot
 deduce template function from argument types !()(int,void)


 Do you think it's a good idea to allow it?

 Bye,
 bearophile

I consider it necessary.
Dec 07 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 12/07/2012 11:12 PM, bearophile wrote:
 Timon Gehr:

 I consider it necessary.

To consider opening an enhancement request, are you able to explain us why?

It is just embarrassing if it does not work. Similar things work even in C#. Of course, it is harder in D because the type system is Turing complete and stuff. I think we want to support things like: [1,2,3].map(a=>2*a);
 And is it possible to implement it? :-)

Yes; in fact it almost works satisfactorily in my home-grown front end already. As most of D compilation, it is undecidable, but the following heuristic should be fine: 1. Resolve and fix explicit template arguments. 2. Infer types from arguments. Unify. Do not consider known lambda return types at this point. 3. Analyze lambdas whose parameter types are known. Fix those types. 4. Resolve lambda return values where known. Unify. 5. Repeat 2-4 until fixed point is reached. 6. Use default template arguments. 7. Repeat 5-6 until fixed point is reached. 8. Check if everything became known and if the argument types are actually compatible with the inferred signature. (Where 'fix' means, if the type should later be unified with something that is not the same type, fail instantiation.) Furthermore, probably it should treat curried lambdas a little more cleverly than the above. It may be argued that this is too complex. :o)
Dec 07 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Timon Gehr:

 I consider it necessary.

To consider opening an enhancement request, are you able to explain us why? And is it possible to implement it? :-) Bye, bearophile
Dec 07 2012
prev sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Timon Gehr:

 I think we want to support things like:

 [1,2,3].map(a=>2*a);

This was one of my original use cases.
 It may be argued that this is too complex. :o)

Even if they are trying to speed up Scala compilation, it's much slower than Java/D compilation, because the Scala type system is very complex and refined and the compiler has to work a lot. If the D type system gets refined as we are discussing here, maybe the compilation speed of templates will be reduced a little. I have no idea if this is true, and how much. Bye, bearophile
Dec 07 2012