Jump to page: 1 2
Thread overview
static foreach and new identifier names
Jan 05, 2018
Adam D. Ruppe
Jan 05, 2018
H. S. Teoh
Jan 05, 2018
Adam D. Ruppe
Jan 05, 2018
H. S. Teoh
Jan 05, 2018
Adam D. Ruppe
Jan 06, 2018
biozic
Jan 05, 2018
Jonathan M Davis
Jan 05, 2018
Timon Gehr
Jan 05, 2018
Meta
Jan 05, 2018
Meta
Jan 06, 2018
Meta
January 05, 2018
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.
January 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
January 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
January 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.
January 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:
> > 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
January 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:
> > 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

January 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)
January 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;
January 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:
>> 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.
January 06, 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:
>> 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
« First   ‹ Prev
1 2