Thread overview | |||||||
---|---|---|---|---|---|---|---|
|
November 20, 2013 Function template literals - or "just" parameter type inference? | ||||
---|---|---|---|---|
| ||||
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. |
November 20, 2013 Re: Function template literals - or "just" parameter type inference? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jakob Ovrum Attachments:
| 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 |
November 20, 2013 Re: Function template literals - or "just" parameter type inference? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kenji Hara | 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. |
November 20, 2013 Re: Function template literals - or "just" parameter type inference? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jakob Ovrum Attachments:
| 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
|
November 20, 2013 Re: Function template literals - or "just" parameter type inference? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kenji Hara | 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)); |
Copyright © 1999-2021 by the D Language Foundation