Thread overview | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
May 26, 2018 Clash When Using Function as Template Value-Parameters? | ||||
---|---|---|---|---|
| ||||
I've been experimenting with code that uses std.functional : binaryFun and unaryFun, but I have found that using these methods makes it impossible to add function attributes like @safe, @nogc, pure, and nothrow, because no guarantee can be made about the functions created via a stream. For example, if you expect a comparator function like "a == b", someone can pass in "a.data--" instead. That being said, I started trying out using strongly typed and attributed template parameters instead, relying on lambdas to keep the syntax for the user short. But when I tried this, I found that the very existence of templates with different parameter values causes a collision during compilation. The following code snippet demonstrates the error: ``` import std.stdio; final class BTree( ValueT, KeyT = ValueT, const(KeyT) function(ValueT) @safe @nogc nothrow pure KeyF = (a) => a) { KeyT getKey(ValueT val) { return KeyF(val); } } void main() { auto btree1 = new BTree!(char); // Removing this line eliminates the error. auto btree2 = new BTree!(int); } ``` The error is: ``` onlineapp.d(8): Error: function literal `__lambda6(char a)` is not callable using argument types `(int)` onlineapp.d(8): cannot pass argument `val` of type `int` to parameter `char a` onlineapp.d(15): Error: template instance `onlineapp.BTree!(int, int, function (char a) => a)` error instantiating ``` Is this an error in the compiler or in my own understanding of the D language? |
May 27, 2018 Re: Clash When Using Function as Template Value-Parameters? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Vijay Nayar | On Saturday, 26 May 2018 at 11:56:30 UTC, Vijay Nayar wrote:
> The error is:
> ```
> onlineapp.d(8): Error: function literal `__lambda6(char a)` is not callable using argument types `(int)`
> onlineapp.d(8): cannot pass argument `val` of type `int` to parameter `char a`
> onlineapp.d(15): Error: template instance `onlineapp.BTree!(int, int, function (char a) => a)` error instantiating
> ```
Just to clarify. In the example above, if I create a 'BTree!int' by itself, it's fine. If I create a 'BTree!char' by itself, it's fine also. But if I create both, even if they are created in different modules, the compiler seems to mix up the types of the function template-parameter, and tries to fit a 'char' to the 'int' function or an 'int' to the 'char' function, depending on which was declared first.
|
May 27, 2018 Re: Clash When Using Function as Template Value-Parameters? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Vijay Nayar Attachments:
| I would rewrite it to something like this:
import std.stdio;
import std.functional;
template BTree(ValueT, KeyT = ValueT,alias KeyF = unaryFun!"cast(const)a")
{
class BTree
{
auto getKey(ValueT val) {
return KeyF(val);
}
}
}
void main()
{
auto btree1 = new BTree!(char)(); // Removing this line eliminates the
error.
auto btree2 = new BTree!(int)();
}
On Sun, May 27, 2018 at 9:03 AM, Vijay Nayar via Digitalmars-d < digitalmars-d@puremagic.com> wrote:
> On Saturday, 26 May 2018 at 11:56:30 UTC, Vijay Nayar wrote:
>
>> The error is:
>> ```
>> onlineapp.d(8): Error: function literal `__lambda6(char a)` is not
>> callable using argument types `(int)`
>> onlineapp.d(8): cannot pass argument `val` of type `int` to
>> parameter `char a`
>> onlineapp.d(15): Error: template instance `onlineapp.BTree!(int, int,
>> function (char a) => a)` error instantiating
>> ```
>>
>
> Just to clarify. In the example above, if I create a 'BTree!int' by itself, it's fine. If I create a 'BTree!char' by itself, it's fine also. But if I create both, even if they are created in different modules, the compiler seems to mix up the types of the function template-parameter, and tries to fit a 'char' to the 'int' function or an 'int' to the 'char' function, depending on which was declared first.
>
|
May 28, 2018 Re: Clash When Using Function as Template Value-Parameters? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Daniel Kozak | On Sunday, 27 May 2018 at 20:38:25 UTC, Daniel Kozak wrote:
> I would rewrite it to something like this:
>
> template BTree(ValueT, KeyT = ValueT,alias KeyF = unaryFun!"cast(const)a")
> {
> class BTree
> {
This is roughly what I originally had, but it creates a number of problems that I wanted to get around. Changing KeyF back to an alias means that any function that uses it can no longer be const, pure, @nogc, or nothrow. Essentially the parameter is just anything the user provides.
If I use a template value-parameter, then it forces any lambda the user passes in to either match the type I enter in (with const, pure, etc.) or the program to fail to compile. That is, I don't want the user to pass in any function, but only functions with the desired attributes. I.e., I wouldn't want them to pass in for KeyF something like "a.data--".
Listing out the full type does indeed work correctly with various examples, and letting the user pass in something like `a => a._id` does compile, but the only problem is that when there are two such template instances in the same program.
Logically `BTree!(MyStruct, int, a => a.id)`, `BTree!(AnotherStruct, char, a => a.name[0])`, `BTree!int` and `BTree!char` should all be totally independent. But for reasons unknown, the individual parameters seems to be swapped and and confused during compilation.
In the error above I listed. The function parameter from `BTree!char` is being used to create a compile error against `BTree!int`, which is very odd. Each of these classes compile and run just fine individually, the compilation only breaks when both exist.
|
May 29, 2018 Re: Clash When Using Function as Template Value-Parameters? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Vijay Nayar | On Saturday, 26 May 2018 at 11:56:30 UTC, Vijay Nayar wrote:
> I've been experimenting with code that uses std.functional : binaryFun and unaryFun, but I have found that using these methods makes it impossible to add function attributes like @safe, @nogc, pure, and nothrow, because no guarantee can be made about the functions created via a stream. For example, if you expect a comparator function like "a == b", someone can pass in "a.data--" instead.
>
> [...]
This is probably a bug. BTree!char.lambda is clearly not the same as BTree!int.lambda, but the compiler seems to disagree?
|
May 29, 2018 Re: Clash When Using Function as Template Value-Parameters? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Yuxuan Shui | On Tuesday, 29 May 2018 at 11:34:03 UTC, Yuxuan Shui wrote:
> On Saturday, 26 May 2018 at 11:56:30 UTC, Vijay Nayar wrote:
>> I've been experimenting with code that uses std.functional : binaryFun and unaryFun, but I have found that using these methods makes it impossible to add function attributes like @safe, @nogc, pure, and nothrow, because no guarantee can be made about the functions created via a stream. For example, if you expect a comparator function like "a == b", someone can pass in "a.data--" instead.
>>
>> [...]
>
> This is probably a bug. BTree!char.lambda is clearly not the same as BTree!int.lambda, but the compiler seems to disagree?
No, wait a second. (a)=>a is in default argument list, so it is in the global scope. And it was instantiated when you instantiate BTree with char.
|
May 29, 2018 Re: Clash When Using Function as Template Value-Parameters? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Yuxuan Shui | On Tuesday, 29 May 2018 at 11:36:11 UTC, Yuxuan Shui wrote:
>
> No, wait a second. (a)=>a is in default argument list, so it is in the global scope. And it was instantiated when you instantiate BTree with char.
Could you explain that part a bit for me? Yes, (a) => a is a default value, but when you say it is in the global scope, are you saying that a single object "(a) => a" is created in the global scope and not created for each template argument list, e.g. "BTree!int" and "BTree!char"?
I actually do not know in what scope such objects would be created, I had assumed it was per template-parameter list, but are you saying this is not the case?
|
May 29, 2018 Re: Clash When Using Function as Template Value-Parameters? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Vijay Nayar | On Tuesday, 29 May 2018 at 12:37:04 UTC, Vijay Nayar wrote:
> On Tuesday, 29 May 2018 at 11:36:11 UTC, Yuxuan Shui wrote:
>>
>> No, wait a second. (a)=>a is in default argument list, so it is in the global scope. And it was instantiated when you instantiate BTree with char.
>
> Could you explain that part a bit for me? Yes, (a) => a is a default value, but when you say it is in the global scope, are you saying that a single object "(a) => a" is created in the global scope and not created for each template argument list, e.g. "BTree!int" and "BTree!char"?
>
> I actually do not know in what scope such objects would be created, I had assumed it was per template-parameter list, but are you saying this is not the case?
I believe that is the case. Normally that will be fine, because you can't modify them. Type-deduced lambda is a very special case, as in their parameter types are deduced on first use, so in a sense, they are "modified" by the first instantiation.
BTW, I can't find the documentation about defining lambda with their parameter types omitted anywhere.
|
May 29, 2018 Re: Clash When Using Function as Template Value-Parameters? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Yuxuan Shui | On Tuesday, 29 May 2018 at 12:58:20 UTC, Yuxuan Shui wrote:
> I believe that is the case. Normally that will be fine, because you can't modify them. Type-deduced lambda is a very special case, as in their parameter types are deduced on first use, so in a sense, they are "modified" by the first instantiation.
>
> BTW, I can't find the documentation about defining lambda with their parameter types omitted anywhere.
I tried this again, this time completely ignoring lambdas and completely specifying the desired type like so:
final class BTree(
ValueT,
KeyT = ValueT,
const(KeyT) function(ValueT) nothrow pure @nogc KeyF =
function KeyT(ValueT a) { return a; }) {
KeyT getKey(ValueT val) {
return KeyF(val);
}
}
But unfortunately, the following code still produces an error:
void main()
{
auto btree1 = new BTree!(char);
auto btree2 = new BTree!(int); // The error is on this line.
}
onlineapp.d(17): Error: template instance `BTree!int` does not match template declaration `BTree(ValueT, KeyT = ValueT, const(char) function(char) pure nothrow @nogc KeyF = function KeyT(ValueT a)
{
return a;
}
)`
I think at this point this may be a bug in the compiler. What do you think?
|
May 30, 2018 Re: Clash When Using Function as Template Value-Parameters? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Vijay Nayar | On Tuesday, 29 May 2018 at 19:17:37 UTC, Vijay Nayar wrote: > On Tuesday, 29 May 2018 at 12:58:20 UTC, Yuxuan Shui wrote: > >> [...] > > I tried this again, this time completely ignoring lambdas and completely specifying the desired type like so: > > [...] Issue created: https://issues.dlang.org/show_bug.cgi?id=18917 |
Copyright © 1999-2021 by the D Language Foundation