digitalmars.D - static foreach and new identifier names
- Adam D. Ruppe (58/58) Jan 05 2018 Y'all know if we write this:
- H. S. Teoh (57/69) Jan 05 2018 [...]
- Adam D. Ruppe (9/11) Jan 05 2018 That's illegal outside of functions, though, which is the place
- H. S. Teoh (8/19) Jan 05 2018 What about my second idea of using a template to create new identifiers
- Adam D. Ruppe (14/16) Jan 05 2018 You're still using a nested scope, which is illegal outside a
- biozic (27/31) Jan 05 2018 Does this do what you want? (without UDA @magic)
- Jonathan M Davis (15/26) Jan 05 2018 In the few cases that I've needed to do something like that, I've ended ...
- Timon Gehr (6/30) Jan 05 2018 See "limitations" section of DIP 1010 for my thoughts:
- Meta (4/7) Jan 05 2018 You can emulate it by abusing the compiler-generated random names
Y'all know if we write this: static foreach(foo; [1,2,3]) { int blargh = foo; } we get: e2.d(2): Error: variable e2.__anonymous.blargh conflicts with variable e2.__anonymous.blargh at e2.d(2) e2.d(2): Error: variable e2.__anonymous.blargh conflicts with variable e2.__anonymous.blargh at e2.d(2) because it expands to int blargh = 1; int blargh = 2; int blargh = 3; all in the same scope. This makes static foreach of fairly little value to me - most times I have considered using it, I end up just using plain old string mixin instead so i can generate names for the declarations. Well, how's this for a solution? Any time the compiler is expecting an identifier in a declaration, it can see the `mixin` keyword instead and expand that. The mixin expression must yield a string after passing through ctfe. import std.conv; static foreach(foo; [1,2,3]) { // so freaking ugly. but would now be legal: int mixin("\"blargh\"" ~ to!string(foo)) = foo; } --- int mixin("foo"); // illegal, it returned a symbol foo that doesn't exist --- enum foo = "bar"; int mixin("foo"); // now legal, would define `int bar;` --- int mixin(`"foo"`); // legal, same as `int foo;` I find this to be pretty freaking hideous... but it is also the best idea I've had so far to rescue static foreach's value in making new declarations. It limits the string mixin part to just the name, I *think* the parser can handle it easily enough, and it gives all the ctfe flexibility we could use (including outside static foreach btw). What do you all think? ALTERNATIVE IDEA: Make a special identifier known the compiler, let's call it `__unique_name` which is unique for any static foreach iteration. static foreach(foo; [1, 2, 3]) { int __unique_name = foo; } now compiles, since each iteration gives a new magic unique name. But how do you refer to that in the outside world? With alias: static foreach(foo; [1, 2, 3]) { int __unique_name = foo; mixin("alias " ~ ctfe_generated_name ~ " = __unique_name"); } In a little example like this, that looks silly, you might as well just mixin the whole declaration, but for something like a large function, the mixin remains just that one same alias thing, while the rest of it is written normally. The alias will then be responsible for things like merging overloads, if necessary.
Jan 05 2018
On Fri, Jan 05, 2018 at 05:41:23PM +0000, Adam D. Ruppe via Digitalmars-d wrote:Y'all know if we write this: static foreach(foo; [1,2,3]) { int blargh = foo; } we get: e2.d(2): Error: variable e2.__anonymous.blargh conflicts with variable e2.__anonymous.blargh at e2.d(2) e2.d(2): Error: variable e2.__anonymous.blargh conflicts with variable e2.__anonymous.blargh at e2.d(2)[...] Simple solution: add another pair of {} to create a nested scope: static foreach(foo; [1,2,3]) {{ int blargh = foo; }} Well, perhaps this looks just as hideous to your eyes, but at least we didn't have to resort to mixins. :-D OTOH, if you actually need to insert declarations into the containing scope that depends on these temporary declarations, then this won't work. I recently ran into a similar issue, not 100% sure whether it should be considered a bug/enhancement request, but basically: static foreach (x; blah) {{ struct S { ... // declarations that depend on x } S s; ... // do something with s }} Initially, I tried it without the double braces, but of course that doesn't work because S is declared multiple times with incompatible definitions. But even with the double braces, the compiler complained "identifier S is already declared in another scope". :-( Apparently, it's illegal to reuse the same identifier in the same function, even though each declaration is actually in its own disjoint scope. Two solutions occurred to me at the time: (1) create a new identifier namespace with a nested function. I found this far too ugly, so I ditched the idea. (2) Use a mixin to create new names for each declaration, which is what you proposed. But that meant putting a lot of code inside a string, which is extremely ugly (even if D does have token strings, which alleviated the ugliness a little bit, but only a little bit). Then suddenly it struck me: (1) What we need is a way to create new identifiers that depends on some compile-time entity, like the loop variable of a static foreach. (2) A template instantiated with some compile-time entity as argument creates a new, unique name for that particular template instantiation. Put these two together, and Bingo!: struct S(x) { // or alias x, depending on what x is ... // declarations that depend on x } static foreach (x; blah) {{ S!x s; // <--- this is the magic right here ... // do something with s }} Problem solved. :-D In your case, you might be able to do something similar: struct MyInt(int foo) { int blargh = foo; alias blargh this; } static foreach(foo; [1,2,3]) {{ MyInt!foo blargh; }} alias this FTW! T -- Tell me and I forget. Teach me and I remember. Involve me and I understand. -- Benjamin Franklin
Jan 05 2018
On Friday, 5 January 2018 at 18:07:54 UTC, H. S. Teoh wrote:Simple solution: add another pair of {} to create a nested scope:That's illegal outside of functions, though, which is the place static foreach is special! Also, introducing a new scope means you can't expose that stuff to the outside world, which defeats the point. (I had tried doing that and using the alias outside but still fails.) Note that my declarations here are NOT temporary - the reason I'm trying to use static foreach is to introduce new public members to the scope.
Jan 05 2018
On Fri, Jan 05, 2018 at 06:33:52PM +0000, Adam D. Ruppe via Digitalmars-d wrote:On Friday, 5 January 2018 at 18:07:54 UTC, H. S. Teoh wrote:What about my second idea of using a template to create new identifiers for intermediate declarations? That way, you can still introduce new declarations to the outer scope that depend on these intermediate declarations. T -- Truth, Sir, is a cow which will give [skeptics] no more milk, and so they are gone to milk the bull. -- Sam. JohnsonSimple solution: add another pair of {} to create a nested scope:That's illegal outside of functions, though, which is the place static foreach is special! Also, introducing a new scope means you can't expose that stuff to the outside world, which defeats the point. (I had tried doing that and using the alias outside but still fails.) Note that my declarations here are NOT temporary - the reason I'm trying to use static foreach is to introduce new public members to the scope.
Jan 05 2018
On Friday, 5 January 2018 at 18:36:52 UTC, H. S. Teoh wrote:What about my second idea of using a template to create new identifiers for intermediate declarations?You're still using a nested scope, which is illegal outside a function. Moreover, what I want to do is introduce functions right here. Consider the following (pseudocode): private magic int foo_() { return 0; } static foreach(func; getByUda!magic()) { public int nameof!func[0 .. $-1]() { return func(); } } The idea is to take a list of inputs - functions with the magic annotation - and create wrapper functions for them with the same name, just without the tailing _. (for one example)
Jan 05 2018
On Friday, 5 January 2018 at 22:25:22 UTC, Adam D. Ruppe wrote:The idea is to take a list of inputs - functions with the magic annotation - and create wrapper functions for them with the same name, just without the tailing _. (for one example)Does this do what you want? (without UDA magic) import std.conv; import std.meta; import std.stdio; import std.traits; enum nameWithout_(alias a) = __traits(identifier, a)[0 .. $ - 1]; struct FunWrap { static auto caller(alias func)(Parameters!func args) { args[0]++; return func(args); } } static foreach (func; AliasSeq!(foo_, bar_)) { mixin("alias " ~ nameWithout_!func ~ " = FunWrap.caller!func;"); } private string foo_(int i) { return "foo says " ~ i.to!string; } private string bar_(int i) { return "bar says " ~ i.to!string; } void main() { writeln(foo(42)); // "foo says 43" writeln(bar(0)); // "bar says 1" }
Jan 05 2018
On Friday, January 05, 2018 18:33:52 Adam D. Ruppe via Digitalmars-d wrote:On Friday, 5 January 2018 at 18:07:54 UTC, H. S. Teoh wrote:In the few cases that I've needed to do something like that, I've ended up using string mixins for the names. I've only ever needed anything like that for unit tests though. The few times that I've used static foreach outside of a function has been inside a template where I inserted static assertions for each type in a variadic template, which was faster than writing a template to use with allSatisfy. But even with a language feature for handling the naming, I don't know how you'd cleanly deal with any of that if you then need to actually refer to those variables elsewhere. But clearly, you've come up with a use case that I've never had. Most of my uses of foreach with AliasSeq (and now using static foreach) have simply been for generating unit tests. It's pretty rare that I've even used them inside a normal function let alone wanted to create anything outside of a function. - Jonathan M DavisSimple solution: add another pair of {} to create a nestedscope:That's illegal outside of functions, though, which is the place static foreach is special! Also, introducing a new scope means you can't expose that stuff to the outside world, which defeats the point. (I had tried doing that and using the alias outside but still fails.) Note that my declarations here are NOT temporary - the reason I'm trying to use static foreach is to introduce new public members to the scope.
Jan 05 2018
On 05.01.2018 18:41, Adam D. Ruppe wrote:Y'all know if we write this: static foreach(foo; [1,2,3]) { int blargh = foo; } we get: e2.d(2): Error: variable e2.__anonymous.blargh conflicts with variable e2.__anonymous.blargh at e2.d(2) e2.d(2): Error: variable e2.__anonymous.blargh conflicts with variable e2.__anonymous.blargh at e2.d(2) because it expands to int blargh = 1; int blargh = 2; int blargh = 3; all in the same scope. This makes static foreach of fairly little value to me - most times I have considered using it, I end up just using plain old string mixin instead so i can generate names for the declarations. ...See "limitations" section of DIP 1010 for my thoughts: https://github.com/dlang/DIPs/blob/master/DIPs/DIP1010.md (Mixin identifiers are among them. You might also like __local declarations.) Also see: http://forum.dlang.org/post/ooo9kt$1dnf$1 digitalmars.com
Jan 05 2018
On Friday, 5 January 2018 at 17:41:23 UTC, Adam D. Ruppe wrote:Make a special identifier known the compiler, let's call it `__unique_name` which is unique for any static foreach iteration.You can emulate it by abusing the compiler-generated random names for lambdas: enum uniqueName(string cookie = {}.stringof) = cookie;
Jan 05 2018
On Friday, 5 January 2018 at 23:50:52 UTC, Meta wrote:On Friday, 5 January 2018 at 17:41:23 UTC, Adam D. Ruppe wrote:But that won't work for what you want. Never mind me.Make a special identifier known the compiler, let's call it `__unique_name` which is unique for any static foreach iteration.You can emulate it by abusing the compiler-generated random names for lambdas: enum uniqueName(string cookie = {}.stringof) = cookie;
Jan 05 2018
On Friday, 5 January 2018 at 23:52:43 UTC, Meta wrote:On Friday, 5 January 2018 at 23:50:52 UTC, Meta wrote:Oho, template mixins to the rescue. With this you can auto generate all the new symbols you want and the syntax isn't too ugly. mixin template uniqueName(DeclType, string cookie = {}.stringof) { mixin(`DeclType ` ~ cookie ~ `;`); pragma(msg, cookie); } void main() { static foreach (i; 0..50) { mixin uniqueName!int; mixin uniqueName!int; } } This prints: __lambda5 __lambda6 __lambda7 __lambda8 __lambda9 __lambda10 __lambda11 __lambda12 __lambda13 __lambda14 __lambda15 __lambda16 __lambda17 __lambda18 __lambda19 __lambda20 __lambda21 __lambda22 __lambda23 __lambda24 __lambda25 __lambda26 __lambda27 __lambda28 __lambda29 __lambda30 __lambda31 __lambda32 __lambda33 __lambda34 __lambda35 __lambda36 __lambda37 __lambda38 __lambda39 __lambda40 __lambda41 __lambda42 __lambda43 __lambda44 __lambda45 __lambda46 __lambda47 __lambda48 __lambda49 __lambda50 __lambda51 __lambda52 __lambda53 __lambda54 __lambda55 __lambda56 __lambda57 __lambda58 __lambda59 __lambda60 __lambda61 __lambda62 __lambda63 __lambda64 __lambda65 __lambda66 __lambda67 __lambda68 __lambda69 __lambda70 __lambda71 __lambda72 __lambda73 __lambda74 __lambda75 __lambda76 __lambda77 __lambda78 __lambda79 __lambda80 __lambda81 __lambda82 __lambda83 __lambda84 __lambda85 __lambda86 __lambda87 __lambda88 __lambda89 __lambda90 __lambda91 __lambda92 __lambda93 __lambda94 __lambda95 __lambda96 __lambda97 __lambda98 __lambda99 __lambda100 __lambda101 __lambda102 __lambda103 __lambda104On Friday, 5 January 2018 at 17:41:23 UTC, Adam D. Ruppe wrote:But that won't work for what you want. Never mind me.Make a special identifier known the compiler, let's call it `__unique_name` which is unique for any static foreach iteration.You can emulate it by abusing the compiler-generated random names for lambdas: enum uniqueName(string cookie = {}.stringof) = cookie;
Jan 05 2018