www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Function template literals - or "just" parameter type inference?

reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
As I was preparing a pull request to fill in some documentation, 
I became unsure of a couple of things.

It's clear that the Parameter[1] syntactic element does not 
account for the form `InOut(opt) Identifier`, used when the 
parameter type is left to inference. However, this form is only 
allowed in function/delegate literal expressions, not in 
declarations of named functions. So, perhaps adding this form to 
Parameter is incorrect, as it's referenced for both. Do we need 
to define a new kind of parameter list for FunctionLiteral[2] in 
order to account for this extra form?

Further, parameter type inference is implemented in terms of 
(anonymous) function templates. However, the documentation only 
mentions parameter type inference once, in the following 
sentence, under FunctionLiteral[2]:

 If the type of a function literal can be uniquely determined 
 from its context, the[sic] parameter type inference is possible.
Followed by this example: --- void foo(int function(int) fp); void test() { int function(int) fp = (n) { return n * 2; }; // The type of parameter n is inferred to int. foo((n) { return n * 2; }); // The type of parameter n is inferred to int. } --- There is no mention of function templates; is it just an implementation detail, or something we should document? If we ever implemented it in some other way, would it not have consequences for code that can take function templates by alias and possibly call the same function template several times but with completely different argument types, which is currently valid and possibly useful? [1] http://dlang.org/declaration.html#Parameter [2] http://dlang.org/expression.html#FunctionLiteral P.S. The examples in the language documentation often use widely inconsistent brace style, and I also found several examples using capitalization that differs from the Phobos style. Maybe we should aim to normalize them to use the Phobos style.
Nov 19 2013
parent reply Kenji Hara <k.hara.pg gmail.com> writes:
2013/11/20 Jakob Ovrum <jakobovrum gmail.com>

 As I was preparing a pull request to fill in some documentation, I became
 unsure of a couple of things.

 It's clear that the Parameter[1] syntactic element does not account for
 the form `InOut(opt) Identifier`, used when the parameter type is left to
 inference. However, this form is only allowed in function/delegate literal
 expressions, not in declarations of named functions. So, perhaps adding
 this form to Parameter is incorrect, as it's referenced for both. Do we
 need to define a new kind of parameter list for FunctionLiteral[2] in order
 to account for this extra form?
With the lambda `(const x){ ... } `, the lambda parameter name `x` is parsed as TypeIdentifier. Then semantic analysis handle the `x` as a lambda parameter name that requires type inference. Similarly, with `void foo(const x) {}`, `x` is parsed as TypeIdentifier. However, semantic analysis will handle it as a Type name of unnamed function parameter. The difference is in semantic analysis phase, but not in parsing phase. Therefore the current Parameter grammar is correct.
 Further, parameter type inference is implemented in terms of (anonymous)
 function templates. However, the documentation only mentions parameter type
 inference once, in the following sentence, under FunctionLiteral[2]:

  If the type of a function literal can be uniquely determined from its
 context, the[sic] parameter type inference is possible.
Followed by this example: --- void foo(int function(int) fp); void test() { int function(int) fp = (n) { return n * 2; }; // The type of parameter n is inferred to int. foo((n) { return n * 2; }); // The type of parameter n is inferred to int. } --- There is no mention of function templates; is it just an implementation detail, or something we should document? If we ever implemented it in some other way, would it not have consequences for code that can take function templates by alias and possibly call the same function template several times but with completely different argument types, which is currently valid and possibly useful?
Do you use the word "function template" as meaning function/delegate/lambda literal which requires parameter type inference? If so, it is intended that current documentation has no mention about template lambdas. Indeed, current dmd implements such lambdas by using anonymous templates. As the result, following code is accepted. template X(alias fun) { void test() { fun(1); // OK fun!int(1); // funny, but current dmd accept this. } } void main() { X!(a=>1).test(); // The lambda expression `a=>1` is mostly same as // `template __lambda(__T){ auto __lambda(__T a) { return 1; } }` } I was not sure that the fun!int is legitimate usage. Therefore, I didn't mention about the 'template lambda' semantics in documentation. Therefore currently, it is merely an implementation detail. Kenji Hara
Nov 19 2013
parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 20 November 2013 at 07:52:30 UTC, Kenji Hara wrote:
 With the lambda `(const x){ ... } `, the lambda parameter name 
 `x` is
 parsed as TypeIdentifier.
 Then semantic analysis handle the `x` as a lambda parameter 
 name that
 requires type inference.

 Similarly, with `void foo(const x) {}`, `x` is parsed as 
 TypeIdentifier.
 However, semantic analysis will handle it as a Type name of 
 unnamed
 function parameter.

 The difference is in semantic analysis phase, but not in parsing
 phase. Therefore the current Parameter grammar is correct.
Thank you, that's very interesting.
 Do you use the word "function template" as meaning 
 function/delegate/lambda
 literal which requires parameter type inference?
Yes, my mistake. Named function templates are of course not affected by this issue.
 I was not sure that the fun!int is legitimate usage.
I also ask this question. I think it may prove useful, and code in the wild might be relying on it.
 Therefore, I didn't mention about the 'template lambda'
 semantics in documentation.
Right. My guess is that it will either take many years before this implementation-specific behaviour causes a problem, or alternatively, before it is finally decided that the behaviour should be elevated to become part of the specification. Either way, it's probably not worth spending time on this problem right now.
Nov 20 2013
parent reply Kenji Hara <k.hara.pg gmail.com> writes:
2013/11/20 Jakob Ovrum <jakobovrum gmail.com>

  I was not sure that the fun!int is legitimate usage.

 I also ask this question. I think it may prove useful, and code in the
 wild might be relying on it.
One of the known issue is, that the specified type parameters may not match exactly to the function parameters position. You can make a partially specialized lambda like follows. X!((int a, b) => a + b) Then, inside X(alias fun), fun!long means instantiate (int a, long b){ return a + b; }. And of course, fun!(int, int) will be invalid. So, specifying type parameter for the template lambda will make the template code unreliable. I think it is not good feature in general. Kenji Hara
Nov 20 2013
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 11/20/2013 09:23 AM, Kenji Hara wrote:
 2013/11/20 Jakob Ovrum <jakobovrum gmail.com <mailto:jakobovrum gmail.com>>


         I was not sure that the fun!int is legitimate usage.


     I also ask this question. I think it may prove useful, and code in
     the wild might be relying on it.


 One of the known issue is, that the specified type parameters may not
 match exactly to the function parameters position.

 You can make a partially specialized lambda like follows.

 X!((int a, b) => a + b)

 Then, inside X(alias fun), fun!long means instantiate (int a, long b){
 return a + b; }.
 And of course, fun!(int, int) will be invalid.
 ...
The requirements are supposed to be captured by the template constraint.
 So, specifying type parameter for the template lambda will make the
 template code unreliable.
Typically one will be able to pass any arbitrary function template in there too, and for those there can be _arbitrary_ functional dependencies between the template arguments and the parameter types. Relying on any correspondence is invalid in general.
 I think it is not good feature in general.

 Kenji Hara
The entities declared as 'foo' and 'bar' below are similar enough that it does not make sense to treat them differently in this aspect. auto foo(T)(int x, T y){ return y; } // --- template ID(alias a){ alias ID=a; } alias bar=ID!((int x,y)=>y); pragma(msg, foo!int(1,2)); pragma(msg, bar!int(1,2));
Nov 20 2013