static foreach considered

Idan Arye via Digitalmars-d digitalmars-d at puremagic.com
Mon Jun 8 16:50:52 PDT 2015


On Monday, 8 June 2015 at 22:16:50 UTC, Timon Gehr wrote:
> On 06/09/2015 12:12 AM, Idan Arye wrote:
>> On Monday, 8 June 2015 at 21:32:52 UTC, Timon Gehr wrote:
>>> I think the body should have access to a scope that is hidden 
>>> from the
>>> outside which contains the loop variable, but declarations 
>>> should be
>>> inserted into the enclosing scope like for static if.
>>
>> This would require some syntax to mark the declarations we 
>> want to
>> expose. Maybe `out`?
>>
>> This is far better than the mixin template approach, since 
>> it'll alert
>> us early about conflicts:
>>
>>     static foreach (ident; ["a", "b", "a"])
>>     {
>>         int mixin(ident ~ "1");
>>         out int mixin(ident ~ "2");
>>     }
>>
>> `a1` is created twice, but that's OK since it isn't marked 
>> with `out`.
>> `a2` is declared twice and raises a compilation error because 
>> it's
>> marked with `out`. This will ensure these kind of errors are 
>> detected
>> early and the compilation error points to the exact place of 
>> declaration.
>
> I actually intended all declarations in the body to be inserted 
> into the enclosing scope, at least by default.

What about helper declarations that repeat in each static 
iteration? It can work like with mixin templates, where 
declarations hide each other(http://dpaste.dzfl.pl/c173395eb0cd), 
but that means that if there is a repeat in the declaration you 
do want to expose, the compiler will simply hide it without 
issuing an error. You will get an error when you try to access 
that declaration from somewhere else, but this error message is 
distant from the root cause both in time - you might only write 
the code that access the declaration created by that particular 
iteration much later in the development process - and space - the 
error will point to the point of usage, not the point of 
duplicate declaration. Also, if the point of usage is inside a 
template and depends on the template instantiation this kind of 
error is much harder to debug...

As for exposing the declaration by default - unless there is a 
backward compatibility issue, it's usually best to make the most 
restrictive and contained version the default one. If not 
exposing is the default and someone neglects to mark the exposed 
declaration, it will fail immediately when they try to access 
it(and they will. Immediately. Because that's the code they are 
writing right now) and they can just add the annotation. But if 
exposing is the default, and someone neglects to mark the 
internal helpers as non-exposed, well - they better hope that 
there are duplications that'll expose their mistake. This is not 
always trivial:

     struct Foo(Types...)
     {
         static foreach (Type; Types)
         {
             static if (isSomeString!Type)
             {
                 // I forgot to mark this as non-exposed
                 void stringHelper()
                 {
                     // helper for strings
                 }

                 void doSomething(Type arg)
                 {
                     // Something that uses stringHelper
                 }
             }
             else
             {
                 void doSomething(Type arg)
                 {
                     // The non-string versio
                 }
             }
         }
     }

     unittest
     {
         alias MyFoo = Foo!(int, float, string);
         // some tests with MyFoo
     }

Since I only have one string in MyFoo's types list, 
`stringHelper` is only declared once. A month from now, when I 
try to create `Foo!(string, wstring)`, it'll create two 
`stringHelper`s and result in compilation error. Having an error 
show up a month later is not fun. It's much less fun when it it 
pops for someone else that now needs to figure out what you were 
trying to do...

That's why I think not exposing should be the default. In that 
case, since `doSomething` is not marked as exposed, this will 
fail early, because we can safely assume the exposed 
functionality is being tested - even if I don't write a proper 
unit test, since `doSomething` is part of `Foo`'s API I will try 
to use it(if you write API without at least trying it out you 
deserve whatever method of torture the people that use that API 
can think of), so the bug will pop early.


More information about the Digitalmars-d mailing list