www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - static foreach and new identifier names

reply Adam D. Ruppe <destructionator gmail.com> writes:
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
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
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
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
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:
 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.
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. Johnson
Jan 05
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
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
parent biozic <dransic gmail.com> writes:
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
prev sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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:
 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.
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 Davis
Jan 05
prev sibling next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
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
prev sibling parent reply Meta <jared771 gmail.com> writes:
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
parent reply Meta <jared771 gmail.com> writes:
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:
 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;
But that won't work for what you want. Never mind me.
Jan 05
parent Meta <jared771 gmail.com> writes:
On Friday, 5 January 2018 at 23:52:43 UTC, Meta wrote:
 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:
 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;
But that won't work for what you want. Never mind me.
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 __lambda104
Jan 05