digitalmars.D - Function template literals - or "just" parameter type inference?
- Jakob Ovrum (38/40) Nov 19 2013 As I was preparing a pull request to fill in some documentation,
- Kenji Hara (29/60) Nov 19 2013 With the lambda `(const x){ ... } `, the lambda parameter name `x` is
- Jakob Ovrum (13/32) Nov 20 2013 Yes, my mistake. Named function templates are of course not
- Kenji Hara (12/16) Nov 20 2013 One of the known issue is, that the specified type parameters may not ma...
- Timon Gehr (14/30) Nov 20 2013 Typically one will be able to pass any arbitrary function template in
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
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 itsDo 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 Haracontext, 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?
Nov 19 2013
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
2013/11/20 Jakob Ovrum <jakobovrum gmail.com>I was not sure that the fun!int is legitimate usage.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 HaraI also ask this question. I think it may prove useful, and code in the wild might be relying on it.
Nov 20 2013
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 HaraThe 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