www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Infer function template parameters

reply "Jonas Drewsen" <jdrewsen nospam.com> writes:
In foreach statements the type can be inferred:

foreach (MyFooBar fooBar; fooBars) writeln(fooBar);
same as:
foreach (foobar; fooBars) writeln(fooBar);

This is nice and tidy.
Wouldn't it make sense to allow the same for function templates 
as well:

auto min(L,R)(L a, R b)
{
     return a < b;
}

same as:

auto min(a,b)
{
     return a < b;
}

What am I missing (except some code that needs chaging because 
only param type and not name has been specified in t?

/Jonas
Sep 20 2012
next sibling parent "Jonas Drewsen" <jdrewsen nospam.com> writes:
On Thursday, 20 September 2012 at 19:56:48 UTC, Jonas Drewsen 
wrote:
 In foreach statements the type can be inferred:

Clicked the send butten too early by mistake but I guess you get the idea. /Jonas
Sep 20 2012
prev sibling next sibling parent reply "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Thursday, 20 September 2012 at 19:56:48 UTC, Jonas Drewsen 
wrote:
 Wouldn't it make sense to allow the same for function templates 
 as well: <snip>

 What am I missing (except some code that needs chaging because 
 only param type and not name has been specified in t?

I can't see any implementation issues with it, but I think templates should be an explicit choice when writing functions. Like it or not, templates still cause a lot of code bloat, complicate linking, cannot be virtual, increase compilation resources, and generate difficult to understand messages. They are a powerful tool, but need to be used wisely.
Sep 20 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 09/20/2012 10:52 PM, Peter Alexander wrote:
 On Thursday, 20 September 2012 at 19:56:48 UTC, Jonas Drewsen wrote:
 Wouldn't it make sense to allow the same for function templates as
 well: <snip>

 What am I missing (except some code that needs chaging because only
 param type and not name has been specified in t?

I can't see any implementation issues with it, but I think templates should be an explicit choice when writing functions.

Leaving out the parameter type is an explicit choice.
 Like it or not, templates still cause a lot of code bloat, complicate
 linking, cannot be virtual, increase compilation resources, and generate
 difficult to understand messages. They are a powerful tool, but need to
 be used wisely.

The proposal does not make wise usage harder. It only makes usage more concise in some cases.
Sep 20 2012
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 09/20/2012 09:57 PM, Jonas Drewsen wrote:
 ...
 What am I missing (except some code that needs chaging because only
 param type and not name has been specified in [i]t?

Nothing, that is about it. (C backwards-compatibility could maybe be added) Of course, we could make upper case identifiers indicate parameters without name and lower case identifiers indicate parameters with templated types, keeping the breakages at a minimum. :o) Note that other language changes would have to be made, eg: void main(){ int delegate(int) dg1 = x=>x; // currently ok, should stay ok auto foo(T)(T x){ return x; } int delegate(int) dg2 = &foo; // currently error, would become ok } (x=>x would become a template delegate literal following your proposal)
Sep 20 2012
prev sibling next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, September 20, 2012 21:57:47 Jonas Drewsen wrote:
 In foreach statements the type can be inferred:
 
 foreach (MyFooBar fooBar; fooBars) writeln(fooBar);
 same as:
 foreach (foobar; fooBars) writeln(fooBar);
 
 This is nice and tidy.
 Wouldn't it make sense to allow the same for function templates
 as well:
 
 auto min(L,R)(L a, R b)
 {
 return a < b;
 }
 
 same as:
 
 auto min(a,b)
 {
 return a < b;
 }
 
 What am I missing (except some code that needs chaging because
 only param type and not name has been specified in t?

You don't want everything templated. Templated functions are fundamentally different. They don't exist until they're instantiated, and they're only instantiated because you call them. Sometimes, you want functions to always exist regardless of whether any of your code is calling them (particularly when dealing with libraries). Another result of all of this is that templated functions can't be virtual, so your proposal would be a _huge_ problem for classes. Not to mention, errors with templated functions tend to be much nastier than with non-templated functions even if it's not as bad as C++. Also, your prosposal then means that we'd up with templated functions without template constraints as a pretty normal thing, which would mean that such functions would frequently get called with types that don't work with them. To fix that, you'd have to add template constraints to such functions, which would be even more verbose than just giving the types like we do now. You really need to be able to control when something is templated or not. And your proposal is basically just a terser template syntax. Is it really all that more verbose to do auto min(L, R)(L a, R b) {...} rather than auto min(a, b) {...} And even if we added your syntax, we'd still need the current syntax, because you need to able to indicate which types go with which parameters even if it's just to say that two parameters have the same type. Also, what happens if you put types on some parameters but not others? Are those parameters given templated types? If so, a simple type could silently turn your function into a templated function without you realizing it. Then there's function overloading. If you wanted to overload a function in your proposal, you'd have to either still give the types or use template constraints, meaning that it can't be used with overloaded functions. Another thing to consider is that in languages like Haskell where all parameter types are inferred, it's often considered good practice to give the types anyway (assuming that the language lets you - Haskell does), because the functions are then not only easier to understand, but the error messages are more sane. So, I really don't think that this is a good idea. It's just a terser, less flexible, and more error-prone syntax for templates. - Jonathan M Davis
Sep 20 2012
parent deadalnix <deadalnix gmail.com> writes:
I'll add that delegate literals already allow a similar syntax, so, for 
consistency reasons, this is something that make sense.
Sep 21 2012
prev sibling next sibling parent "Jonas Drewsen" <jdrewsen nospam.com> writes:
On Thursday, 20 September 2012 at 21:39:31 UTC, Jonathan M Davis 
wrote:
 On Thursday, September 20, 2012 21:57:47 Jonas Drewsen wrote:
 In foreach statements the type can be inferred:
 
 foreach (MyFooBar fooBar; fooBars) writeln(fooBar);
 same as:
 foreach (foobar; fooBars) writeln(fooBar);
 
 This is nice and tidy.
 Wouldn't it make sense to allow the same for function templates
 as well:
 
 auto min(L,R)(L a, R b)
 {
 return a < b;
 }
 
 same as:
 
 auto min(a,b)
 {
 return a < b;
 }
 
 What am I missing (except some code that needs chaging because
 only param type and not name has been specified in t?

You don't want everything templated. Templated functions are fundamentally different. They don't exist until they're instantiated, and they're only instantiated because you call them. Sometimes, you want functions to always exist regardless of whether any of your code is calling them (particularly when dealing with libraries).

I agree. And in that case just use a non-templated version that specifies the types as always.
 Another result of all of this is that templated functions can't 
 be virtual, so
 your proposal would be a _huge_ problem for classes. Not to 
 mention, errors
 with templated functions tend to be much nastier than with 
 non-templated
 functions even if it's not as bad as C++.

I don't see how the terser syntax for templated functions has anything to do with this. The things you mention are simply facts about templated functions and nothing special for the suggested syntax.
 Also, your prosposal then means that
 we'd up with templated functions without template constraints 
 as a pretty
 normal thing, which would mean that such functions would 
 frequently get called
 with types that don't work with them. To fix that, you'd have 
 to add template
 constraints to such functions, which would be even more verbose 
 than just
 giving the types like we do now.

By looking at the two examples I provided, both the existing syntax and the new one suffers from that. The new one is just nicer on the eyes I think.
 You really need to be able to control when something is 
 templated or not. And
 your proposal is basically just a terser template syntax. Is it 
 really all
 that more verbose to do

 auto min(L, R)(L a, R b) {...}

 rather than

 auto min(a, b) {...}

Some people would love to be able to use D as a scripting language using e.g. rdmd. This is the kind of thing that would make it very attractive for scripting. I am _not_ suggesting to replace the existing syntax since that really should be used for things like phobos where everything must be checked by the type system as much as possible upfront. But for many programs (especially in the prototyping/exploratory phases) the same kind of thoroughness is not within the resource limits. That is probably why many use dynamically typed languages like python/ruby for prototyping and first editions and end up sticking with those languages in the end. D has already taken great steps in that direction and this is just a suggestion to make it even more attractive.
 And even if we added your syntax, we'd still need the current 
 syntax, because
 you need to able to indicate which types go with which 
 parameters even if it's
 just to say that two parameters have the same type.

As mentioned before this suggestion is an addition. Not a replacement.
 Also, what happens if you put types on some parameters but not 
 others? Are
 those parameters given templated types? If so, a simple type 
 could silently
 turn your function into a templated function without you 
 realizing it.

Maybe I wasn't clear in my suggestion. The new syntax in simply a way to define a templated function - not a non-templated one ie: auto foo(a,b) {} is exactly the same as auto foo(A,B)(A a, B b) {} The semantic of what should happen if one of the parameters had its type provided is up for discussion. But I think that it should be allowed and just lock that template parameter to that type. This would not change it from templated to non-templated in any case afaik.
 Then there's function overloading. If you wanted to overload a 
 function in
 your proposal, you'd have to either still give the types or use 
 template
 constraints, meaning that it can't be used with overloaded 
 functions.

Yes. As with the the existing template syntax.
 Another thing to consider is that in languages like Haskell 
 where all
 parameter types are inferred, it's often considered good 
 practice to give the
 types anyway (assuming that the language lets you - Haskell 
 does), because the
 functions are then not only easier to understand, but the error 
 messages are
 more sane.

And the good practice is done on stable code or when you are sure about what you are doing from the start. I do not think it is uncommon to try out a solution and refactor and iterate until things gets nice and tidy. This is definitely not the only way to work, but for some problem domains (especially the ones you are not well versed in yet) this is not uncommon. That claim is my own :) I guess "productivity" could be a buzz word to put in here if I were into buzzwords. /Jonas
Sep 21 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, September 21, 2012 13:14:56 Jonas Drewsen wrote:
 Maybe I wasn't clear in my suggestion. The new syntax in simply a
 way to define a templated function - not a non-templated one ie:
 
 auto foo(a,b) {}
 is exactly the same as
 auto foo(A,B)(A a, B b) {}

So all it does is save you a few characters? I don't think that that's even vaguely worth it. It complicates the language and doesn't add any functionality whatsoever. And when you consider that it then makes it _harder_ to quickly see that a function is templated, and it potentially makes it easier to accidentally templatize a function, I think that it's a net loss even without considering the fact that it complicates the language further. And _with_ considering it, I think that it's definitely more trouble than it's worth. - Jonathan M Davis
Sep 21 2012
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 20 Sep 2012 15:57:47 -0400, Jonas Drewsen <jdrewsen nospam.com>  
wrote:

 In foreach statements the type can be inferred:

 foreach (MyFooBar fooBar; fooBars) writeln(fooBar);
 same as:
 foreach (foobar; fooBars) writeln(fooBar);

 This is nice and tidy.
 Wouldn't it make sense to allow the same for function templates as well:

 auto min(L,R)(L a, R b)
 {
      return a < b;
 }

 same as:

 auto min(a,b)
 {
      return a < b;
 }

 What am I missing (except some code that needs chaging because only  
 param type and not name has been specified in t?

Although I like it, I wonder if it works in D's context free grammar. Timon probably would know best... I came up with this code, which compiles today: import std.stdio; alias int x; void foo(x) {} void foo2(string x) {writeln(x);} void main() { foo(1); foo2("hello"); } Under your proposal, if we shorten foo2 to foo2(x), what happens? Does it become just like foo? Or does it turn into a template? Or is it an error? Note that just because some syntax isn't valid doesn't mean it should be utilized for a valid use. That can result in code compiling and meaning something completely different than you expect. -Steve
Sep 21 2012
prev sibling next sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Thursday, 20 September 2012 at 21:04:15 UTC, Timon Gehr wrote:
 On 09/20/2012 10:52 PM, Peter Alexander wrote:
 Like it or not, templates still cause a lot of code bloat, 
 complicate
 linking, cannot be virtual, increase compilation resources, 
 and generate
 difficult to understand messages. They are a powerful tool, 
 but need to
 be used wisely.

The proposal does not make wise usage harder. It only makes usage more concise in some cases.

Conciseness encourages use, both wise and unwise. I don't think that templates should have more concise syntax than non-templates. Having shorter syntax suggests that it should be the default choice, and it's a bad default choice for most functions.
Sep 21 2012
prev sibling next sibling parent "jerro" <a a.com> writes:
 Although I like it, I wonder if it works in D's context free 
 grammar.  Timon probably would know best...

 I came up with this code, which compiles today:

 import std.stdio;
 alias int x;

 void foo(x) {}

 void foo2(string x) {writeln(x);}

 void main()
 {
     foo(1);
     foo2("hello");
 }

 Under your proposal, if we shorten foo2 to foo2(x), what 
 happens?  Does it become just like foo?  Or does it turn into a 
 template?  Or is it an error?

I don't see any way the proposed syntax could work either. We could have this, though: auto min(auto a, auto b) { return a < b; } But I don't think this feature is worth changing the language anyway.
Sep 21 2012
prev sibling next sibling parent "Jonas Drewsen" <jdrewsen nospam.com> writes:
On Friday, 21 September 2012 at 11:40:54 UTC, Jonathan M Davis 
wrote:
 On Friday, September 21, 2012 13:14:56 Jonas Drewsen wrote:
 Maybe I wasn't clear in my suggestion. The new syntax in 
 simply a
 way to define a templated function - not a non-templated one 
 ie:
 
 auto foo(a,b) {}
 is exactly the same as
 auto foo(A,B)(A a, B b) {}

So all it does is save you a few characters? I don't think that that's even vaguely worth it. It complicates the language and doesn't add any functionality whatsoever.

Correct. The same with foreach where you also just save some characters but it is darn nice anyway.
 And when you consider that it then makes it _harder_ to quickly 
 see that a
 function is templated, and it potentially makes it easier to 
 accidentally
 templatize a function, I think that it's a net loss even 
 without considering
 the fact that it complicates the language further. And _with_ 
 considering it,
 I think that it's definitely more trouble than it's worth.

Fair enough. -Jonas
Sep 21 2012
prev sibling next sibling parent "Jonas Drewsen" <jdrewsen nospam.com> writes:
On Friday, 21 September 2012 at 15:04:14 UTC, Steven 
Schveighoffer wrote:
 On Thu, 20 Sep 2012 15:57:47 -0400, Jonas Drewsen 
 <jdrewsen nospam.com> wrote:

 In foreach statements the type can be inferred:

 foreach (MyFooBar fooBar; fooBars) writeln(fooBar);
 same as:
 foreach (foobar; fooBars) writeln(fooBar);

 This is nice and tidy.
 Wouldn't it make sense to allow the same for function 
 templates as well:

 auto min(L,R)(L a, R b)
 {
     return a < b;
 }

 same as:

 auto min(a,b)
 {
     return a < b;
 }

 What am I missing (except some code that needs chaging because 
 only param type and not name has been specified in t?

Although I like it, I wonder if it works in D's context free grammar. Timon probably would know best... I came up with this code, which compiles today: import std.stdio; alias int x; void foo(x) {}

This would not be a valid syntax in my proposal since x is not a parameter name as it should be, but a type name.
 void foo2(string x) {writeln(x);}

 void main()
 {
     foo(1);
     foo2("hello");
 }

 Under your proposal, if we shorten foo2 to foo2(x), what 
 happens?  Does it become just like foo?  Or does it turn into a 
 template?  Or is it an error?

A mentioned in the proposal (albeit not very clear) it requires non-templated function definitions to include both type and param names. If only one name is provided in a definition is always a param name. Unfortunately this is a breaking change for some code and that does speak against the proposal.
 Note that just because some syntax isn't valid doesn't mean it 
 should be utilized for a valid use.  That can result in code 
 compiling and meaning something completely different than you 
 expect.

I agree.
Sep 21 2012
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On 2012-09-21, 21:29, Jonas Drewsen wrote:

 A mentioned in the proposal (albeit not very clear) it requires  
 non-templated function definitions to include both type and param names.  
 If only one name is provided in a definition is always a param name.  
 Unfortunately this is a breaking change for some code and that does  
 speak against the proposal.

Not only is it a breaking change, it breaks one of the basic design desiderata of D - if it's valid C it either fails to compile or compiles with the same behavior as in C. -- Simen
Sep 22 2012
prev sibling next sibling parent "Jonas Drewsen" <jdrewsen nospam.com> writes:
On Saturday, 22 September 2012 at 07:48:14 UTC, Simen Kjaeraas 
wrote:
 On 2012-09-21, 21:29, Jonas Drewsen wrote:

 A mentioned in the proposal (albeit not very clear) it 
 requires non-templated function definitions to include both 
 type and param names. If only one name is provided in a 
 definition is always a param name. Unfortunately this is a 
 breaking change for some code and that does speak against the 
 proposal.

Not only is it a breaking change, it breaks one of the basic design desiderata of D - if it's valid C it either fails to compile or compiles with the same behavior as in C.

I guess it is not that basic of a desiderata after all :) http://www.prowiki.org/wiki4d/wiki.cgi?LanguageDevel/DIPs/DIP19 -Jonas
Sep 24 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, September 24, 2012 10:37:04 Jonas Drewsen wrote:
 On Saturday, 22 September 2012 at 07:48:14 UTC, Simen Kjaeraas
 
 wrote:
 On 2012-09-21, 21:29, Jonas Drewsen wrote:
 A mentioned in the proposal (albeit not very clear) it
 requires non-templated function definitions to include both
 type and param names. If only one name is provided in a
 definition is always a param name. Unfortunately this is a
 breaking change for some code and that does speak against the
 proposal.

Not only is it a breaking change, it breaks one of the basic design desiderata of D - if it's valid C it either fails to compile or compiles with the same behavior as in C.

I guess it is not that basic of a desiderata after all :) http://www.prowiki.org/wiki4d/wiki.cgi?LanguageDevel/DIPs/DIP19

It has been broken upon rare occasion (e.g. static arrays are value types in D, not reference types like in C), but it's quite rare, and removing the comera operator doesn't necessarily break it. In fact, just removing the comma operator _definitely_ doesn't break it, because it just makes more C code invalid. The point is that C/C++ will compile as valid D code with the same semantics or that it won't compile, _not_ that C/C++ will compile as valid D code. The whole point is to avoid silent behavioral changes when porting C/C++ code to D. Now, that being said, if tuples are added to the language proper (which that DIP doesn't do), that may or may not make it so that C/C++ code will end up compiling with different semantics when ported. I'd have to study the situation much more closely to be sure, but I suspect that it wouldn't, precisely because the types involved change and C doesn't have auto in the same way that D does (or any kind of type inferrence at all really), so the change in type would cause compilation failure, thereby avoiding silent behavioral changes. - Jonathan M Davis
Sep 24 2012
prev sibling next sibling parent "Jonas Drewsen" <jdrewsen nospam.com> writes:
On Monday, 24 September 2012 at 10:05:49 UTC, Jonathan M Davis 
wrote:
 On Monday, September 24, 2012 10:37:04 Jonas Drewsen wrote:
 On Saturday, 22 September 2012 at 07:48:14 UTC, Simen Kjaeraas
 
 wrote:
 On 2012-09-21, 21:29, Jonas Drewsen wrote:
 A mentioned in the proposal (albeit not very clear) it
 requires non-templated function definitions to include both
 type and param names. If only one name is provided in a
 definition is always a param name. Unfortunately this is a
 breaking change for some code and that does speak against 
 the
 proposal.

Not only is it a breaking change, it breaks one of the basic design desiderata of D - if it's valid C it either fails to compile or compiles with the same behavior as in C.

I guess it is not that basic of a desiderata after all :) http://www.prowiki.org/wiki4d/wiki.cgi?LanguageDevel/DIPs/DIP19

It has been broken upon rare occasion (e.g. static arrays are value types in D, not reference types like in C), but it's quite rare, and removing the comera operator doesn't necessarily break it. In fact, just removing the comma operator _definitely_ doesn't break it, because it just makes more C code invalid. The point is that C/C++ will compile as valid D code with the same semantics or that it won't compile, _not_ that C/C++ will compile as valid D code. The whole point is to avoid silent behavioral changes when porting C/C++ code to D. Now, that being said, if tuples are added to the language proper (which that DIP doesn't do), that may or may not make it so that C/C++ code will end up compiling with different semantics when ported. I'd have to study the situation much more closely to be sure, but I suspect that it wouldn't, precisely because the types involved change and C doesn't have auto in the same way that D does (or any kind of type inferrence at all really), so the change in type would cause compilation failure, thereby avoiding silent behavioral changes. - Jonathan M Davis

What about: int fun() { return (0, "abc")[0]; } in the comma operator case it would return 'a' as an int. in the tuple case it would return 0 /Jonas
Sep 24 2012
prev sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Monday, September 24, 2012 19:18:09 Jonas Drewsen wrote:
 What about:
 
 int fun() {
 return (0, "abc")[0];
 }
 
 in the comma operator case it would return 'a' as an int.
 in the tuple case it would return 0

Like I said, I'd have to examine the situation more closely to be certain that there are no places where C code would compile but change semantics. It looks like you found one. That may or may not be enough to make it so that tuples wouldn't be done that way. On rare occasions, C compatability has been broken in this regard, but it's very rare. So, it's a very strong rule, but it's not unbreakable. Also, it's already been suggested in the thread on DIP 19 that we just give different syntax to tuples to fix the problem. And it's not at all clear that there's agreement that adding tuples to the language buys enough to make it worth it anyway. So, who knows what will happen. - Jonathan M Davis
Sep 24 2012