digitalmars.D - Puzzled
- Don Allen (48/48) Dec 12 2023 Since ceasing my use of D out of concern for the direction and
- monkyyy (31/36) Dec 12 2023 Your imagining aliases as words rather than references to
- Don Allen (68/104) Dec 12 2023 I regard templates as a form of more hygienic macros than simple
- Max Samukha (24/26) Dec 12 2023 This works (the explicit template is due to a 'need this' bug):
- Walter Bright (17/49) Dec 12 2023 The trouble here is that `s` is a local variable of `main`. The `bletch!...
- Don Allen (15/67) Dec 13 2023 Note that in the message to which you are replying, I sent two
- Don Allen (5/55) Dec 13 2023 I'm going to add a guess here: the field references passed by
- Walter Bright (2/5) Dec 13 2023 Right.
- Don Allen (6/12) Dec 13 2023 Walter -- I wanted to thank you for your responses. They have
- Walter Bright (2/6) Dec 14 2023 You're welcome!
- Walter Bright (2/8) Dec 13 2023 Yes, but the compiler does not have a way to semantically connect the tw...
- Petar Kirov [ZombineDev] (32/35) Dec 13 2023 Try this, it works since dmd 2.087:
- Timon Gehr (32/60) Dec 12 2023 This defines three symbols:
- monkyyy (3/6) Dec 12 2023 No that will break eventually because strings are not reliable
- Max Samukha (2/7) Dec 12 2023 `__traits(child, ...)` works sometimes.
- Timon Gehr (2/13) Dec 13 2023 Thanks!
- Walter Bright (14/44) Dec 12 2023 bar is looked up in this scope, meaning it is looked up in main(), and t...
- Don Allen (10/21) Dec 13 2023 [snip]
- monkyyy (21/22) Dec 13 2023 you should use a mixin instead of traits that use strings to
Since ceasing my use of D out of concern for the direction and health of the project, I've nonetheless been following these forums and have seen what I consider to be sincere efforts to improve things. So I've started doing a bit of D work again and ran into this: ```` import std.stdio; struct Foo { int bar; int baz; } void bletch(alias field)(Foo s, int x) { s.field = x; writeln("%d", s.field); } int main(string[] args) { Foo s; bletch!bar(s); bletch!baz(s); return 0; } ```` Compiling with dmd 2.106.0 on an up-to-date NixOS system results in ```` (dmd-2.106.0)dca giovanni:/home/dca/Software/d_tests$ dmd test.d test.d(16): Error: undefined identifier `bar` test.d(17): Error: undefined identifier `baz` ```` So it appears that symbols passed to a function template alias parameter need to be in scope at the instantiation site as opposed to within the instantiated code. This makes no sense to me. The symbols at the instantiation site are not being evaluated in that scope. They will be evaluated by the compiler when it gets its hands on the instantiated code. If a simple compile-time substitution were done in the above code, those symbols would both be defined in the instantiated code. Reading the documentation, I'm guessing that template mixins can be used to address this problem, so it's probably the case (I haven't tried it) that I can achieve what I want that way. But I'm failing to understand why the problem exists in the first place. Perhaps inherited from C++, which I do not know, having had some early exposure to it and concluded it was awful? I'm probably missing something here, perhaps some important cases where the behavior I'm seeing *does* make sense. I'd be interested in the comments of those who know D (and perhaps C++) better than I do.
Dec 12 2023
On Tuesday, 12 December 2023 at 19:02:11 UTC, Don Allen wrote:I'm probably missing something here, perhaps some important cases where the behavior I'm seeing *does* make sense. I'd be interested in the comments of those who know D (and perhaps C++) better than I do.Your imagining aliases as words rather than references to definitions As far as I know, aliases are optimized to work with "overload sets" ```d void foo(int); void foo(bool); template bar(alias A){} bar!foo; ``` A is now all foo's, foo's need to exist, and foo mostly pretends to stop knowing where it comes from do: ```d struct Foo { int bar; int baz; } void bletch(alias field)(int x) { field = x; writeln("%d", s.field); } int main(string[] args) { Foo s; bletch!(s.bar); bletch!(s.baz); return 0; } ```
Dec 12 2023
On Tuesday, 12 December 2023 at 19:50:25 UTC, monkyyy wrote:On Tuesday, 12 December 2023 at 19:02:11 UTC, Don Allen wrote:I regard templates as a form of more hygienic macros than simple text substitution. To me, they are a form of meta-programming -- code writing code. My view may be wrong and if so, I'd like to know why. If not, I have not seen an answer that explains why a symbol passed to an alias parameter needs a definition in the scope of the template instantiation, as opposed to within the instantiated template body. What you have suggested above is a way to make my little example work. While less interested in that, I'm not un-interested :-) So I played with this a bit. Taking your approach, cleaned up a bit (the bletch calls need the int argument), I tried ```` import std.stdio; struct Foo { int bar; int baz; } void bletch(alias field)(int n) { field = n; writeln("%d", field); } int main(string[] args) { Foo s; bletch!(s.bar)(5); bletch!(s.baz)(6); return 0; } ```` This results in ```` [nix-shell:~/Software/d_tests]$ dmd test.d test.d(16): Error: calling non-static function `bletch` requires an instance of type `Foo` test.d(17): Error: calling non-static function `bletch` requires an instance of type `Foo` (dmd-2.106.0) ```` Since field is aliased to s.bar or s.baz, I tried this: ```` import std.stdio; struct Foo { int bar; int baz; } void bletch(alias field)(Foo s, int n) { field = n; writeln("%d", field); } int main(string[] args) { Foo s; bletch!(s.bar)(s, 5); bletch!(s.baz)(s, 6); return 0; } ```` Same complaints from the compiler: ```` [nix-shell:~/Software/d_tests]$ dmd test.d test.d(16): Error: calling non-static function `bletch` requires an instance of type `Foo` test.d(17): Error: calling non-static function `bletch` requires an instance of type `Foo` (dmd-2.106.0) ````I'm probably missing something here, perhaps some important cases where the behavior I'm seeing *does* make sense. I'd be interested in the comments of those who know D (and perhaps C++) better than I do.Your imagining aliases as words rather than references to definitions As far as I know, aliases are optimized to work with "overload sets" ```d void foo(int); void foo(bool); template bar(alias A){} bar!foo; ``` A is now all foo's, foo's need to exist, and foo mostly pretends to stop knowing where it comes from do: ```d struct Foo { int bar; int baz; } void bletch(alias field)(int x) { field = x; writeln("%d", s.field); } int main(string[] args) { Foo s; bletch!(s.bar); bletch!(s.baz); return 0; } ```
Dec 12 2023
On Wednesday, 13 December 2023 at 03:37:25 UTC, Don Allen wrote:Same complaints from the compiler: ````This works (the explicit template is due to a 'need this' bug): ``` import std.stdio; struct Foo { int bar; int baz; } template bletch(alias field) { static void bletch(T)(ref T self, int x) { auto pField = &__traits(child, self, field); *pField = x; writefln("%d", *pField); } } int main(string[] args) { Foo s; bletch!(s.bar)(s, 1); bletch!(s.baz)(s, 2); return 0; } ```
Dec 12 2023
On 12/12/2023 7:37 PM, Don Allen wrote:```` import std.stdio; struct Foo { int bar; int baz; } void bletch(alias field)(Foo s, int n) { field = n; writeln("%d", field); } int main(string[] args) { Foo s; bletch!(s.bar)(s, 5); bletch!(s.baz)(s, 6); return 0; } ```` Same complaints from the compiler: ```` [nix-shell:~/Software/d_tests]$ dmd test.d test.d(16): Error: calling non-static function `bletch` requires an instance of type `Foo` test.d(17): Error: calling non-static function `bletch` requires an instance of type `Foo` (dmd-2.106.0) ````The trouble here is that `s` is a local variable of `main`. The `bletch!(s.bar)` is passing `s` not by reference, and not by value, but by symbol. Inside the body of `bletch`, there is no way that it can get at the runtime value of the symbol `main.s`. Hence, the complaint that `bletch` requires a way to get at the instance of `main.s`. D has 3 ways of passing arguments: 1. by value 2. by pointer (aka by reference) 3. by symbol (aka by alias) Passing by symbol is strictly a compile time phenomenon, it cannot be a runtime one. C++'s method of passing by alias is called a "template template parameter", and is limited to passing the names of template symbols. D generalized it so any symbol could be passed that way. C macros can have arguments, and they are always passed by name, which is roughly similar to by alias. They are also a compile-time phenomenon, not a runtime one.
Dec 12 2023
On Wednesday, 13 December 2023 at 07:05:10 UTC, Walter Bright wrote:On 12/12/2023 7:37 PM, Don Allen wrote:Note that in the message to which you are replying, I sent two attempts to resolve this. In the first, having seen the compilation errors I got, I came to the same conclusion you describe above. In the second, I added a 'Foo s' parameter and passed main.s to it in the two calls to bletch, which I thought would fix this problem, since now there is an s of type Foo defined in the scope of the field references. To my surprise, it didn't.```` import std.stdio; struct Foo { int bar; int baz; } void bletch(alias field)(Foo s, int n) { field = n; writeln("%d", field); } int main(string[] args) { Foo s; bletch!(s.bar)(s, 5); bletch!(s.baz)(s, 6); return 0; } ```` Same complaints from the compiler: ```` [nix-shell:~/Software/d_tests]$ dmd test.d test.d(16): Error: calling non-static function `bletch` requires an instance of type `Foo` test.d(17): Error: calling non-static function `bletch` requires an instance of type `Foo` (dmd-2.106.0) ````The trouble here is that `s` is a local variable of `main`. The `bletch!(s.bar)` is passing `s` not by reference, and not by value, but by symbol. Inside the body of `bletch`, there is no way that it can get at the runtime value of the symbol `main.s`. Hence, the complaint that `bletch` requires a way to get at the instance of `main.s`.D has 3 ways of passing arguments: 1. by value 2. by pointer (aka by reference) 3. by symbol (aka by alias)I assume this is much like passing a quoted symbol in Lisp/Scheme, where the receiving function gets the symbol, not its value, e.g., (foo 'bar)? Your comment below about C macros and passing symbols by alias suggests that is true.Passing by symbol is strictly a compile time phenomenon, it cannot be a runtime one.Understood.C++'s method of passing by alias is called a "template template parameter", and is limited to passing the names of template symbols. D generalized it so any symbol could be passed that way. C macros can have arguments, and they are always passed by name, which is roughly similar to by alias. They are also a compile-time phenomenon, not a runtime one.
Dec 13 2023
On Wednesday, 13 December 2023 at 15:46:12 UTC, Don Allen wrote:On Wednesday, 13 December 2023 at 07:05:10 UTC, Walter Bright wrote:I'm going to add a guess here: the field references passed by alias to bletch are to main.s.bar and main.s.baz, so passing 's' to bletch at runtime doesn't help, since the field references are not to bletch.s.bar, etc.On 12/12/2023 7:37 PM, Don Allen wrote:Note that in the message to which you are replying, I sent two attempts to resolve this. In the first, having seen the compilation errors I got, I came to the same conclusion you describe above. In the second, I added a 'Foo s' parameter and passed main.s to it in the two calls to bletch, which I thought would fix this problem, since now there is an s of type Foo defined in the scope of the field references. To my surprise, it didn't.```` import std.stdio; struct Foo { int bar; int baz; } void bletch(alias field)(Foo s, int n) { field = n; writeln("%d", field); } int main(string[] args) { Foo s; bletch!(s.bar)(s, 5); bletch!(s.baz)(s, 6); return 0; } ```` Same complaints from the compiler: ```` [nix-shell:~/Software/d_tests]$ dmd test.d test.d(16): Error: calling non-static function `bletch` requires an instance of type `Foo` test.d(17): Error: calling non-static function `bletch` requires an instance of type `Foo` (dmd-2.106.0) ````The trouble here is that `s` is a local variable of `main`. The `bletch!(s.bar)` is passing `s` not by reference, and not by value, but by symbol. Inside the body of `bletch`, there is no way that it can get at the runtime value of the symbol `main.s`. Hence, the complaint that `bletch` requires a way to get at the instance of `main.s`.
Dec 13 2023
On 12/13/2023 7:55 AM, Don Allen wrote:I'm going to add a guess here: the field references passed by alias to bletch are to main.s.bar and main.s.baz, so passing 's' to bletch at runtime doesn't help, since the field references are not to bletch.s.bar, etc.Right.
Dec 13 2023
On Wednesday, 13 December 2023 at 17:59:14 UTC, Walter Bright wrote:On 12/13/2023 7:55 AM, Don Allen wrote:Walter -- I wanted to thank you for your responses. They have helped me to better understand what's happening. Perhaps I'm thinking of templates too much as a macro pre-processor; I clearly need to spend more time with template documentation.I'm going to add a guess here: the field references passed by alias to bletch are to main.s.bar and main.s.baz, so passing 's' to bletch at runtime doesn't help, since the field references are not to bletch.s.bar, etc.Right.
Dec 13 2023
On 12/13/2023 2:31 PM, Don Allen wrote:Walter -- I wanted to thank you for your responses. They have helped me to better understand what's happening. Perhaps I'm thinking of templates too much as a macro pre-processor; I clearly need to spend more time with template documentation.You're welcome!
Dec 14 2023
On 12/13/2023 7:46 AM, Don Allen wrote:Note that in the message to which you are replying, I sent two attempts to resolve this. In the first, having seen the compilation errors I got, I came to the same conclusion you describe above. In the second, I added a 'Foo s' parameter and passed main.s to it in the two calls to bletch, which I thought would fix this problem, since now there is an s of type Foo defined in the scope of the field references. To my surprise, it didn't.Yes, but the compiler does not have a way to semantically connect the two.
Dec 13 2023
On Wednesday, 13 December 2023 at 03:37:25 UTC, Don Allen wrote:On Tuesday, 12 December 2023 at 19:50:25 UTC, monkyyy wrote:Try this, it works since dmd 2.087: ```d struct Foo { int bar; double baz; void bletch(alias field)(typeof(field) newValue) { field = newValue; } } void main(string[] args) { import std.stdio; Foo s; s.bletch!(Foo.bar)(3); s.bletch!(s.baz)(4); // this also works - the same as above writeln(s); auto setBar = &s.bletch!(Foo.bar); auto setBaz = &s.bletch!(Foo.baz); pragma (msg, typeof(setBar)); pragma (msg, typeof(setBaz)); setBar(18); setBaz(19.5); writeln(s); } ``` By making `bletch` a member function of `Foo` it will naturally have "an instance of type `Foo`" - the implicit `this` parameter that all member functions receive. Afterwards, you can see that we can take the address of the member function template instance `&s.bletch!(Foo.bar)` which results in a `delegate` (and not a function pointer). This allows us to call `bletch` through this delegate as it will supply `s` as the context pointer.On Tuesday, 12 December 2023 at 19:02:11 UTC, Don Allen wrote:[..]
Dec 13 2023
On 12/12/23 20:02, Don Allen wrote:Since ceasing my use of D out of concern for the direction and health of the project, I've nonetheless been following these forums and have seen what I consider to be sincere efforts to improve things. So I've started doing a bit of D work again and ran into this: ```` import std.stdio; struct Foo { int bar; int baz; } ...This defines three symbols: Foo Foo.bar Foo.baz There is however no `bar` and no `baz` at module scope.void bletch(alias field)(Foo s, int x) { s.field = x; writeln("%d", s.field); } ...This attempts to access `s.field`, but `field` is not a member of `Foo`.int main(string[] args) { Foo s; bletch!bar(s); bletch!baz(s); return 0; } ```` ...This find neither `bar` nor `baz`, because those are nested in `Foo`. There are symbols `Foo.bar` and `Foo.baz`, but not `bar` and `baz`. The idiomatic way to achieve what you want is this: ```d import std.stdio; struct Foo{ int bar; int baz; } void bletch(string field)(Foo s, int x){ __traits(getMember, s, field) = x; writeln(__traits(getMember, s, field)); } void main(){ Foo s; bletch!"bar"(s, 1); bletch!"baz"(s, 2); } ``` Arguably this is not very nice, but it does the job. You can pass `Foo.bar` and `Foo.baz` as aliases, but I think currently there is no good way to access a field of an object based on an alias to the field symbol. (Other than `__traits(getMember, self, __traits(identifier, field))`, but that bypasses all the typechecking you might get anyway.)
Dec 12 2023
On Tuesday, 12 December 2023 at 21:48:59 UTC, Timon Gehr wrote:The idiomatic way to achieve what you want is this: __traits(getMember, s, field) = x;No that will break eventually because strings are not reliable references; also __traits are ugly and for other abstractions
Dec 12 2023
On Tuesday, 12 December 2023 at 21:48:59 UTC, Timon Gehr wrote:You can pass `Foo.bar` and `Foo.baz` as aliases, but I think currently there is no good way to access a field of an object based on an alias to the field symbol. (Other than `__traits(getMember, self, __traits(identifier, field))`, but that bypasses all the typechecking you might get anyway.)`__traits(child, ...)` works sometimes.
Dec 12 2023
On 12/13/23 07:31, Max Samukha wrote:On Tuesday, 12 December 2023 at 21:48:59 UTC, Timon Gehr wrote:Thanks!You can pass `Foo.bar` and `Foo.baz` as aliases, but I think currently there is no good way to access a field of an object based on an alias to the field symbol. (Other than `__traits(getMember, self, __traits(identifier, field))`, but that bypasses all the typechecking you might get anyway.)`__traits(child, ...)` works sometimes.
Dec 13 2023
On 12/12/2023 11:02 AM, Don Allen wrote:Since ceasing my use of D out of concern for the direction and health of the project, I've nonetheless been following these forums and have seen what I consider to be sincere efforts to improve things.Welcome back!So I've started doing a bit of D work again and ran into this: ```` import std.stdio; struct Foo { int bar; int baz; } void bletch(alias field)(Foo s, int x) { s.field = x; writeln("%d", s.field); } int main(string[] args) { Foo s; bletch!bar(s); // undefined identifier `bar`bar is looked up in this scope, meaning it is looked up in main(), and then looked up in the module. It is not found, because bar is in the scope of Foo. Hence the error message for this linebletch!baz(s); return 0; } ```` But I'm failing to understand why the problem exists in the first place.The misunderstanding appears to be the assumption that `field` is resolved inside the definition of `bletch`. It is not, it is resolved at the point of instantiation of the template `bletch`.Perhaps inherited from C++, which I do not know, having had some early exposure to it and concluded it was awful?C++ has Argument Dependent Lookup (ADL) which follows different rules that I have largely forgotten, despite having implemented them in my C++ compiler. ADL was invented by Andrew Koenig to get around lookup problems with operator overloading where `this` was on the right hand side of the operator. I never liked ADL, it is confusing and a giant ugly kludge. I know of no other language that has an equivalent.
Dec 12 2023
On Wednesday, 13 December 2023 at 06:52:59 UTC, Walter Bright wrote:On 12/12/2023 11:02 AM, Don Allen wrote:[snip]Since ceasing my use of D out of concern for the direction and health of the project, I've nonetheless been following these forums and have seen what I consider to be sincere efforts to improve things.Welcome back!Not quite. I do understand the above, because it is evident from the behavior I am seeing. What I'm puzzled by is why template processing behaves this way, requiring that the symbol be defined at the point where it is *passed (by symbol)*, rather than where it is *used*, which is the place where having a valid definition really matters. More in my reply to your second message.But I'm failing to understand why the problem exists in the first place.The misunderstanding appears to be the assumption that `field` is resolved inside the definition of `bletch`. It is not, it is resolved at the point of instantiation of the template `bletch`.
Dec 13 2023
On Tuesday, 12 December 2023 at 19:02:11 UTC, Don Allen wrote:you should use a mixin instead of traits that use strings to store references. The issue with either is that strings in different contexts may not keep referencing the same thing. The mixin will just be smaller. ```d void bletch(string field)(ref Foo s,int x){ mixin("s."~field~"=x;"); } ``` if you want to be *idiomatic* you should figure out how to make an alias survive the compiler warnings for your use case and given I started an argument I suggest this is better and least prone to failure ```d void bletch(alias f, string s)(int x){ mixin("f."~s)= x; } bletch!(s,"bar")(2); ```__traits, __traits, __traits, __traits,
Dec 13 2023