digitalmars.D - Extend the call site default argument expansion mechanism?
- Yuxuan Shui (25/25) May 10 2018 So in D I can use default argument like this:
- rikki cattermole (2/35) May 10 2018 Bad idea, too much magic.
- Yuxuan Shui (3/6) May 10 2018 This magic is already there in D. I just want to use it in a
- rikki cattermole (4/11) May 10 2018 The magic is not already in there.
- Timon Gehr (2/16) May 11 2018 Yes, that's essentially the definition of "magic".
- JN (20/45) May 10 2018 For things like this you can use the OOP Factory pattern,
- Yuxuan Shui (4/25) May 10 2018 But doing it with default argument expansion saves you 1
- rikki cattermole (2/31) May 10 2018 class -> struct, now it is back to 1 allocation.
- Uknown (2/11) May 10 2018 Alternatively `scope`d classes would also work with dip1000
- Paul Backus (4/11) May 10 2018 Even easier:
- Yuxuan Shui (4/17) May 11 2018 I think one problem with this and Factory, is that you have to
- Seb (3/6) May 10 2018 Why not define a TLS or global variable like theAllocator?
- Yuxuan Shui (8/16) May 10 2018 Because my proposal is better encapsulated. Definitions of
- Dukc (12/21) May 11 2018 Doesn't this basically mean including the implicits Martin
- Meta (3/16) May 11 2018 Yes it does. I was thinking the exact same thing while watching
- Atila Neves (4/21) May 15 2018 D doesn't have Scala's implicits though. However, one can write
- jmh530 (7/19) May 11 2018 I'm not sure this makes sense or not...but what about instead of
- Jonathan M Davis (13/38) May 11 2018 It seems like really risky move, honestly, because it means that the
- Uknown (20/39) May 11 2018 I see what you're saying and I agree with you. I think a better
- Meta (26/45) May 11 2018 It's not as pretty, and I don't know if it works outside this toy
- Yuxuan Shui (3/30) May 15 2018 I found another alternative to this:
- Steven Schveighoffer (12/50) May 15 2018 Hm... neat idea. Somehow, opDispatch can probably be used to make this
- jmh530 (4/16) May 15 2018 Example:
- Yuxuan Shui (4/23) May 15 2018 Sadly with(WithAlloc!alloc) doesn't work. (If you have to use
- jmh530 (3/9) May 15 2018 Yeah I know, I tried it, but couldn't figure out how to do the
- Steven Schveighoffer (8/31) May 15 2018 It seems opDispatch isn't being used in the with statement. That seems
- Meta (5/15) May 15 2018 Yeah I tried it with opDispatch but it didn't work. I vaguely
- jmh530 (20/28) May 15 2018 Looks like with statements ignore opDispatch.
- jmh530 (6/7) May 15 2018 Note, it's not an issue if Foo were not a struct. This was fixed
- Computermatronic (7/15) May 15 2018 Interestingly, you can get pretty close with a struct nested in a
- Simen =?UTF-8?B?S2rDpnLDpXM=?= (32/63) May 16 2018 You've got bugs in your code: ++x has to fail for the template
- jmh530 (3/25) May 16 2018 Thanks for catching that. Any idea why the original was having
- Simen =?UTF-8?B?S2rDpnLDpXM=?= (6/35) May 16 2018 Seems to me there's an issue with overload priority - we expect
- Nick Treleaven (3/5) May 16 2018 In case anyone isn't aware, this is better written:
So in D I can use default argument like this: int f(int line=__LINE__) {} And because default argument is expanded at call site, f() will be called with the line number of the call site. This is a really clever feature, and I think a similar feature can be useful in other ways. Say I need to construct a bunch of data structure that takes an Allocator argument, I need to do this: ... auto alloc = new SomeAllocator(); auto data1 = new DataStructure(..., alloc); auto data2 = new DataStructure(..., alloc); auto data3 = new DataStructure(..., alloc); ... This looks redundant. But if we have the ability to define more special keywords like __LINE__, we can do something like this: ... // constructor of DataStructure this(Allocator alloc=__ALLOC__) {...} ... auto alloc = new SomeAllocator(); define __ALLOC__ = alloc; // And we don't need to pass alloc everytime ... Is this a good idea?
May 10 2018
On 11/05/2018 2:15 AM, Yuxuan Shui wrote:So in D I can use default argument like this: int f(int line=__LINE__) {} And because default argument is expanded at call site, f() will be called with the line number of the call site. This is a really clever feature, and I think a similar feature can be useful in other ways. Say I need to construct a bunch of data structure that takes an Allocator argument, I need to do this: ... auto alloc = new SomeAllocator(); auto data1 = new DataStructure(..., alloc); auto data2 = new DataStructure(..., alloc); auto data3 = new DataStructure(..., alloc); ... This looks redundant. But if we have the ability to define more special keywords like __LINE__, we can do something like this: ... // constructor of DataStructure this(Allocator alloc=__ALLOC__) {...} ... auto alloc = new SomeAllocator(); define __ALLOC__ = alloc; // And we don't need to pass alloc everytime ... Is this a good idea?Bad idea, too much magic.
May 10 2018
On Thursday, 10 May 2018 at 14:17:50 UTC, rikki cattermole wrote:On 11/05/2018 2:15 AM, Yuxuan Shui wrote:This magic is already there in D. I just want to use it in a different way.[...]Bad idea, too much magic.
May 10 2018
On 11/05/2018 2:20 AM, Yuxuan Shui wrote:On Thursday, 10 May 2018 at 14:17:50 UTC, rikki cattermole wrote:The magic is not already in there. __LINE__ and __MODULE__ are special, they are constants recognized by the compiler and are immediately substituted if not specified.On 11/05/2018 2:15 AM, Yuxuan Shui wrote:This magic is already there in D. I just want to use it in a different way.[...]Bad idea, too much magic.
May 10 2018
On 10.05.2018 16:22, rikki cattermole wrote:On 11/05/2018 2:20 AM, Yuxuan Shui wrote:Yes, that's essentially the definition of "magic".On Thursday, 10 May 2018 at 14:17:50 UTC, rikki cattermole wrote:The magic is not already in there. __LINE__ and __MODULE__ are special, they are constants recognized by the compiler and are immediately substituted if not specified.On 11/05/2018 2:15 AM, Yuxuan Shui wrote:This magic is already there in D. I just want to use it in a different way.[...]Bad idea, too much magic.
May 11 2018
On Thursday, 10 May 2018 at 14:15:18 UTC, Yuxuan Shui wrote:So in D I can use default argument like this: int f(int line=__LINE__) {} And because default argument is expanded at call site, f() will be called with the line number of the call site. This is a really clever feature, and I think a similar feature can be useful in other ways. Say I need to construct a bunch of data structure that takes an Allocator argument, I need to do this: ... auto alloc = new SomeAllocator(); auto data1 = new DataStructure(..., alloc); auto data2 = new DataStructure(..., alloc); auto data3 = new DataStructure(..., alloc); ... This looks redundant. But if we have the ability to define more special keywords like __LINE__, we can do something like this: ... // constructor of DataStructure this(Allocator alloc=__ALLOC__) {...} ... auto alloc = new SomeAllocator(); define __ALLOC__ = alloc; // And we don't need to pass alloc everytime ... Is this a good idea?For things like this you can use the OOP Factory pattern, pseudocode: class DataStructureFactory { this(Allocator alloc) { this.alloc = alloc; } Allocator alloc; DataStructure createDataStructure(...) { return new DataStructure(..., alloc) } } DataStructureFactory factory = new DataStructureFactory(new SomeAllocator()) auto data1 = factory.createDataStructure(...) auto data2 = factory.createDataStructure(...) auto data3 = factory.createDataStructure(...)
May 10 2018
On Thursday, 10 May 2018 at 14:28:39 UTC, JN wrote:On Thursday, 10 May 2018 at 14:15:18 UTC, Yuxuan Shui wrote:But doing it with default argument expansion saves you 1 allocation, has 1 less type, while being just as readable. I think that's a win.[...]For things like this you can use the OOP Factory pattern, pseudocode: class DataStructureFactory { this(Allocator alloc) { this.alloc = alloc; } Allocator alloc; DataStructure createDataStructure(...) { return new DataStructure(..., alloc) } } DataStructureFactory factory = new DataStructureFactory(new SomeAllocator()) auto data1 = factory.createDataStructure(...) auto data2 = factory.createDataStructure(...) auto data3 = factory.createDataStructure(...)
May 10 2018
On 11/05/2018 2:33 AM, Yuxuan Shui wrote:On Thursday, 10 May 2018 at 14:28:39 UTC, JN wrote:class -> struct, now it is back to 1 allocation.On Thursday, 10 May 2018 at 14:15:18 UTC, Yuxuan Shui wrote:But doing it with default argument expansion saves you 1 allocation, has 1 less type, while being just as readable. I think that's a win.[...]For things like this you can use the OOP Factory pattern, pseudocode: class DataStructureFactory { this(Allocator alloc) { this.alloc = alloc; } Allocator alloc; DataStructure createDataStructure(...) { return new DataStructure(..., alloc) } } DataStructureFactory factory = new DataStructureFactory(new SomeAllocator()) auto data1 = factory.createDataStructure(...) auto data2 = factory.createDataStructure(...) auto data3 = factory.createDataStructure(...)
May 10 2018
On Thursday, 10 May 2018 at 14:37:00 UTC, rikki cattermole wrote:On 11/05/2018 2:33 AM, Yuxuan Shui wrote:Alternatively `scope`d classes would also work with dip1000On Thursday, 10 May 2018 at 14:28:39 UTC, JN wrote:class -> struct, now it is back to 1 allocation.On Thursday, 10 May 2018 at 14:15:18 UTC, Yuxuan Shui wrote:But doing it with default argument expansion saves you 1 allocation, has 1 less type, while being just as readable. I think that's a win.[...]
May 10 2018
On Thursday, 10 May 2018 at 14:37:00 UTC, rikki cattermole wrote:On 11/05/2018 2:33 AM, Yuxuan Shui wrote:Even easier: alias createDataStructure = (...) => new DataStructure(..., alloc);On Thursday, 10 May 2018 at 14:28:39 UTC, JN wrote: But doing it with default argument expansion saves you 1 allocation, has 1 less type, while being just as readable. I think that's a win.class -> struct, now it is back to 1 allocation.
May 10 2018
On Thursday, 10 May 2018 at 15:15:03 UTC, Paul Backus wrote:On Thursday, 10 May 2018 at 14:37:00 UTC, rikki cattermole wrote:I think one problem with this and Factory, is that you have to create one alias/lambda/factory type for every type that takes an allocator.On 11/05/2018 2:33 AM, Yuxuan Shui wrote:Even easier: alias createDataStructure = (...) => new DataStructure(..., alloc);On Thursday, 10 May 2018 at 14:28:39 UTC, JN wrote: But doing it with default argument expansion saves you 1 allocation, has 1 less type, while being just as readable. I think that's a win.class -> struct, now it is back to 1 allocation.
May 11 2018
On Thursday, 10 May 2018 at 14:15:18 UTC, Yuxuan Shui wrote:So in D I can use default argument like this: int f(int line=__LINE__) {} [...]Why not define a TLS or global variable like theAllocator? Or if you know it at compile-time as an alias?
May 10 2018
On Thursday, 10 May 2018 at 14:30:49 UTC, Seb wrote:On Thursday, 10 May 2018 at 14:15:18 UTC, Yuxuan Shui wrote:Because my proposal is better encapsulated. Definitions of expanded default arguments are scoped, so in the given example, you can have different __ALLOC__ in different scope, where different allocators might be needed. My proposal is basically an alias, but an alias which is recognized by compiler as subtitution keywords for default arguments (like __LINE__, __FILE__).So in D I can use default argument like this: int f(int line=__LINE__) {} [...]Why not define a TLS or global variable like theAllocator? Or if you know it at compile-time as an alias?
May 10 2018
On Thursday, 10 May 2018 at 14:15:18 UTC, Yuxuan Shui wrote:... // constructor of DataStructure this(Allocator alloc=__ALLOC__) {...} ... auto alloc = new SomeAllocator(); define __ALLOC__ = alloc; // And we don't need to pass alloc everytime ... Is this a good idea?Doesn't this basically mean including the implicits Martin Odersky talked about at Dconf in D? I don't know whether it's a good idea all-in-all, but assuming the arguments can be used as compile-time I can already see a big use case: killing autodecoding without breaking code. Something like: auto front(C, bool disableDecoding = __NODECODE__)(inout C[] string) { static if (disableDecoding) {...} else {...} }
May 11 2018
On Friday, 11 May 2018 at 11:42:07 UTC, Dukc wrote:On Thursday, 10 May 2018 at 14:15:18 UTC, Yuxuan Shui wrote:Yes it does. I was thinking the exact same thing while watching his talk; implicits would be perfect for allocators.... // constructor of DataStructure this(Allocator alloc=__ALLOC__) {...} ... auto alloc = new SomeAllocator(); define __ALLOC__ = alloc; // And we don't need to pass alloc everytime ... Is this a good idea?Doesn't this basically mean including the implicits Martin Odersky talked about at Dconf in D?
May 11 2018
On Friday, 11 May 2018 at 13:22:12 UTC, Meta wrote:On Friday, 11 May 2018 at 11:42:07 UTC, Dukc wrote:D doesn't have Scala's implicits though. However, one can write up a reader monad in the D we have right now. AtilaOn Thursday, 10 May 2018 at 14:15:18 UTC, Yuxuan Shui wrote:Yes it does. I was thinking the exact same thing while watching his talk; implicits would be perfect for allocators.... // constructor of DataStructure this(Allocator alloc=__ALLOC__) {...} ... auto alloc = new SomeAllocator(); define __ALLOC__ = alloc; // And we don't need to pass alloc everytime ... Is this a good idea?Doesn't this basically mean including the implicits Martin Odersky talked about at Dconf in D?
May 15 2018
On Friday, 11 May 2018 at 11:42:07 UTC, Dukc wrote:[snip] Doesn't this basically mean including the implicits Martin Odersky talked about at Dconf in D? I don't know whether it's a good idea all-in-all, but assuming the arguments can be used as compile-time I can already see a big use case: killing autodecoding without breaking code. Something like: auto front(C, bool disableDecoding = __NODECODE__)(inout C[] string) { static if (disableDecoding) {...} else {...} }I'm not sure this makes sense or not...but what about instead of implicits, you allow a template to have type erased parameters, basically to optionally mimic the behavior of Java's generics. That way the allocator could be included in the type and checked at compile-time, but it wouldn't be known at run-time (not sure that's a positive or not).
May 11 2018
On Thursday, May 10, 2018 14:15:18 Yuxuan Shui via Digitalmars-d wrote:So in D I can use default argument like this: int f(int line=__LINE__) {} And because default argument is expanded at call site, f() will be called with the line number of the call site. This is a really clever feature, and I think a similar feature can be useful in other ways. Say I need to construct a bunch of data structure that takes an Allocator argument, I need to do this: ... auto alloc = new SomeAllocator(); auto data1 = new DataStructure(..., alloc); auto data2 = new DataStructure(..., alloc); auto data3 = new DataStructure(..., alloc); ... This looks redundant. But if we have the ability to define more special keywords like __LINE__, we can do something like this: ... // constructor of DataStructure this(Allocator alloc=__ALLOC__) {...} ... auto alloc = new SomeAllocator(); define __ALLOC__ = alloc; // And we don't need to pass alloc everytime ... Is this a good idea?It seems like really risky move, honestly, because it means that the function is then affected by what is and isn't declared within the scope where it's called. __FILE__ and __LINE__ are well-defined as to what they mean. No can declare them to mean something else. You don't have symbol resolution issues or naming conflicts. And they're solving a problem that can't actually be solved without compiler help. However, if you just want to change what arguments get passed to foo within your module, all you have to do is define another foo inside the module and have it forward to the original one with whatever arguments you want. What you're suggesting here seems to introduce name pollution issues without solving anything that can't easily be solved with the language as-is. - Jonathan M Davis
May 11 2018
On Friday, 11 May 2018 at 14:26:21 UTC, Jonathan M Davis wrote:On Thursday, May 10, 2018 14:15:18 Yuxuan Shui via Digitalmars-d wrote:I see what you're saying and I agree with you. I think a better way would be to try and extend the `with` syntax to work with arbitrary functions, rather than only objects. That would make it more useful. So something like: --- void f1(allocator alloc, ...){} void f2(allocator alloc, ...){} ... void fn(allocator alloc, ...){} void main() { with(MyAllocator) { f1(...); f2(...); ... fn(...); } } ---So in D I can use default argument like this: [...] Is this a good idea?It seems like really risky move, honestly, because it means that the function is then affected by what is and isn't declared within the scope where it's called. __FILE__ and __LINE__ are well-defined as to what they mean. No can declare them to mean something else. You don't have symbol resolution issues or naming conflicts. And they're solving a problem that can't actually be solved without compiler help. However, if you just want to change what arguments get passed to foo within your module, all you have to do is define another foo inside the module and have it forward to the original one with whatever arguments you want. What you're suggesting here seems to introduce name pollution issues without solving anything that can't easily be solved with the language as-is. - Jonathan M Davis
May 11 2018
On Friday, 11 May 2018 at 15:03:41 UTC, Uknown wrote:I see what you're saying and I agree with you. I think a better way would be to try and extend the `with` syntax to work with arbitrary functions, rather than only objects. That would make it more useful. So something like: --- void f1(allocator alloc, ...){} void f2(allocator alloc, ...){} ... void fn(allocator alloc, ...){} void main() { with(MyAllocator) { f1(...); f2(...); ... fn(...); } } ---It's not as pretty, and I don't know if it works outside this toy example yet, but you can do: import std.stdio; struct Allocator { auto call(alias F, Args...)(Args args) { return F(this, args); } void deallocateAll() { writeln("deallocateAll"); } } void f1(Allocator a, int n) { writeln("f1"); } void f2(Allocator, string s, double d) { writeln("f2"); } void main() { with (Allocator()) { scope(exit) deallocateAll; call!f1(2); call!f2("asdf", 1.0); } }
May 11 2018
On Friday, 11 May 2018 at 18:55:03 UTC, Meta wrote:On Friday, 11 May 2018 at 15:03:41 UTC, Uknown wrote:I found another alternative to this: https://godbolt.org/g/3Etims[...]It's not as pretty, and I don't know if it works outside this toy example yet, but you can do: import std.stdio; struct Allocator { auto call(alias F, Args...)(Args args) { return F(this, args); } void deallocateAll() { writeln("deallocateAll"); } } void f1(Allocator a, int n) { writeln("f1"); } void f2(Allocator, string s, double d) { writeln("f2"); } void main() { with (Allocator()) { scope(exit) deallocateAll; call!f1(2); call!f2("asdf", 1.0); } }
May 15 2018
On 5/15/18 7:53 AM, Yuxuan Shui wrote:On Friday, 11 May 2018 at 18:55:03 UTC, Meta wrote:Hm... neat idea. Somehow, opDispatch can probably be used to make this work even more generically (untested): struct WithAlloc(alias alloc) { auto opDispatch(string s, Args...)(auto ref Args args) if (__traits(compiles, mixin(s ~ "(args, alloc)"))) { mixin("return " ~ s ~ "(args, alloc);"); } } -SteveOn Friday, 11 May 2018 at 15:03:41 UTC, Uknown wrote:I found another alternative to this: https://godbolt.org/g/3Etims[...]It's not as pretty, and I don't know if it works outside this toy example yet, but you can do: import std.stdio; struct Allocator { auto call(alias F, Args...)(Args args) { return F(this, args); } void deallocateAll() { writeln("deallocateAll"); } } void f1(Allocator a, int n) { writeln("f1"); } void f2(Allocator, string s, double d) { writeln("f2"); } void main() { with (Allocator()) { scope(exit) deallocateAll; call!f1(2); call!f2("asdf", 1.0); } }
May 15 2018
On Tuesday, 15 May 2018 at 13:16:21 UTC, Steven Schveighoffer wrote:[snip] Hm... neat idea. Somehow, opDispatch can probably be used to make this work even more generically (untested): struct WithAlloc(alias alloc) { auto opDispatch(string s, Args...)(auto ref Args args) if (__traits(compiles, mixin(s ~ "(args, alloc)"))) { mixin("return " ~ s ~ "(args, alloc);"); } } -SteveExample: https://run.dlang.io/is/RV2xIH
May 15 2018
On Tuesday, 15 May 2018 at 13:59:37 UTC, jmh530 wrote:On Tuesday, 15 May 2018 at 13:16:21 UTC, Steven Schveighoffer wrote:Sadly with(WithAlloc!alloc) doesn't work. (If you have to use withAlloc.func everywhere, it kind of destroy the point, doesn't it?)[snip] Hm... neat idea. Somehow, opDispatch can probably be used to make this work even more generically (untested): struct WithAlloc(alias alloc) { auto opDispatch(string s, Args...)(auto ref Args args) if (__traits(compiles, mixin(s ~ "(args, alloc)"))) { mixin("return " ~ s ~ "(args, alloc);"); } } -SteveExample: https://run.dlang.io/is/RV2xIH
May 15 2018
On Tuesday, 15 May 2018 at 14:26:48 UTC, Yuxuan Shui wrote:Yeah I know, I tried it, but couldn't figure out how to do the with statement with it.[snip] Example: https://run.dlang.io/is/RV2xIHSadly with(WithAlloc!alloc) doesn't work. (If you have to use withAlloc.func everywhere, it kind of destroy the point, doesn't it?)
May 15 2018
On 5/15/18 10:26 AM, Yuxuan Shui wrote:On Tuesday, 15 May 2018 at 13:59:37 UTC, jmh530 wrote:It seems opDispatch isn't being used in the with statement. That seems like a bug, or maybe a limitation. I'm not sure how "with" works, but I assumed it would try calling as a member, and then if it doesn't work, try the call normally. Probably it's checking to see if it has that member first. Annoying... -SteveOn Tuesday, 15 May 2018 at 13:16:21 UTC, Steven Schveighoffer wrote:Sadly with(WithAlloc!alloc) doesn't work. (If you have to use withAlloc.func everywhere, it kind of destroy the point, doesn't it?)[snip] Hm... neat idea. Somehow, opDispatch can probably be used to make this work even more generically (untested): struct WithAlloc(alias alloc) { auto opDispatch(string s, Args...)(auto ref Args args) if (__traits(compiles, mixin(s ~ "(args, alloc)"))) { mixin("return " ~ s ~ "(args, alloc);"); } } -SteveExample: https://run.dlang.io/is/RV2xIH
May 15 2018
On Tuesday, 15 May 2018 at 14:52:46 UTC, Steven Schveighoffer wrote:Yeah I tried it with opDispatch but it didn't work. I vaguely remember some changes being made to how lookup is done in the past year or so... but I can't find the PR in question.Sadly with(WithAlloc!alloc) doesn't work. (If you have to use withAlloc.func everywhere, it kind of destroy the point, doesn't it?)It seems opDispatch isn't being used in the with statement. That seems like a bug, or maybe a limitation. I'm not sure how "with" works, but I assumed it would try calling as a member, and then if it doesn't work, try the call normally. Probably it's checking to see if it has that member first. Annoying... -Steve
May 15 2018
On Tuesday, 15 May 2018 at 14:52:46 UTC, Steven Schveighoffer wrote:[snip] It seems opDispatch isn't being used in the with statement. That seems like a bug, or maybe a limitation. I'm not sure how "with" works, but I assumed it would try calling as a member, and then if it doesn't work, try the call normally. Probably it's checking to see if it has that member first. Annoying... -SteveLooks like with statements ignore opDispatch. struct Foo(int x) { auto opDispatch(string s)() if (s == "bar") { return x++; } } void main() { int y = 0; with(Foo!1) { y = bar; //error: undefined identifier bar } assert(y == 2); }
May 15 2018
On Tuesday, 15 May 2018 at 15:02:36 UTC, jmh530 wrote:[snip]Note, it's not an issue if Foo were not a struct. This was fixed in Bug 6400 [1]l. The issue is with template instances. I have filed a new enhancement request [2] [1] https://issues.dlang.org/show_bug.cgi?id=6400 [2] https://issues.dlang.org/show_bug.cgi?id=18863
May 15 2018
On Tuesday, 15 May 2018 at 20:31:14 UTC, jmh530 wrote:On Tuesday, 15 May 2018 at 15:02:36 UTC, jmh530 wrote:Interestingly, you can get pretty close with a struct nested in a template function, however it seems that overload resolution does not check opDispatch if a public symbol of the same name exists. this (https://run.dlang.io/is/ZzfWDs) dosen't work, but this (https://run.dlang.io/is/7zvfqc) does (notice SList & DList are not directly visible).[snip]Note, it's not an issue if Foo were not a struct. This was fixed in Bug 6400 [1]l. The issue is with template instances. I have filed a new enhancement request [2] [1] https://issues.dlang.org/show_bug.cgi?id=6400 [2] https://issues.dlang.org/show_bug.cgi?id=18863
May 15 2018
On Tuesday, 15 May 2018 at 15:02:36 UTC, jmh530 wrote:On Tuesday, 15 May 2018 at 14:52:46 UTC, Steven Schveighoffer wrote:You've got bugs in your code: ++x has to fail for the template case, since you're trying to increment a compile-time value. This is what the call to bar is lowered to: Foo!1.opDispatch!"bar"() When you try and compile that, you get these error messages: Error: cannot modify constant 1 Error: template instance `foo.Foo!1.Foo.opDispatch!"bar"` error instantiating In addition, the with-statement in your code refers to the type Foo!1, not an instance of it. Fixed code: struct Foo(int x) { int n = x; auto opDispatch(string s)() if (s == "bar") { n++; return n; } } unittest { int y = 0; with(Foo!1()) { y = bar; // Works! } assert(y == 2); } -- Simen[snip] It seems opDispatch isn't being used in the with statement. That seems like a bug, or maybe a limitation. I'm not sure how "with" works, but I assumed it would try calling as a member, and then if it doesn't work, try the call normally. Probably it's checking to see if it has that member first. Annoying... -SteveLooks like with statements ignore opDispatch. struct Foo(int x) { auto opDispatch(string s)() if (s == "bar") { return x++; } } void main() { int y = 0; with(Foo!1) { y = bar; //error: undefined identifier bar } assert(y == 2); }
May 16 2018
On Wednesday, 16 May 2018 at 09:01:29 UTC, Simen Kjærås wrote:snip] struct Foo(int x) { int n = x; auto opDispatch(string s)() if (s == "bar") { n++; return n; } } unittest { int y = 0; with(Foo!1()) { y = bar; // Works! } assert(y == 2); } -- SimenThanks for catching that. Any idea why the original was having problems?
May 16 2018
On Wednesday, 16 May 2018 at 10:51:51 UTC, jmh530 wrote:On Wednesday, 16 May 2018 at 09:01:29 UTC, Simen Kjærås wrote:Seems to me there's an issue with overload priority - we expect opDispatch to count before globals, but it doesn't. Filed an issue: https://issues.dlang.org/show_bug.cgi?id=18866 -- Simensnip] struct Foo(int x) { int n = x; auto opDispatch(string s)() if (s == "bar") { n++; return n; } } unittest { int y = 0; with(Foo!1()) { y = bar; // Works! } assert(y == 2); } -- SimenThanks for catching that. Any idea why the original was having problems?
May 16 2018
On Tuesday, 15 May 2018 at 15:02:36 UTC, jmh530 wrote:auto opDispatch(string s)() if (s == "bar")In case anyone isn't aware, this is better written: auto opDispatch(string s : "bar")()
May 16 2018