September 29, 2012
On Saturday, 29 September 2012 at 07:04:19 UTC, kenji hara wrote:
> After a while, you may add a global variable in module scope.
>
>   enum E { foo ,bar }
>   int foo = 10;
>   void test(E e) {}
>   void main() {
>     test(foo);  // foo is defined, and look up module scope foo.
>     // Then, now the code is broken implicitly!
>   }
>
> This is a hijacking of local scope, and it is awful.

That code doesn't compile anyway, because the global foo is an int. But you're right, my suggestion doesn't work. Or, there's no way to implement it without breaking existing code. Here's the reason again for completeness sake:

enum E { foo, bar };

void test(E e) {}

int intValue = -1;

void main()
{
    int intValue = 42; // Hides the global intValue.

    E foo = E.bar; // This is fine. A local variable foo hides
                   // E.foo from 'implicit global visibility'.
                   // It's effectively same as hiding a global.

    test(foo); // Calls test(E.bar)
}

-----------------------------------------------------

enum E { foo, bar };

void test(E e) {}

void main()
{
    test(foo); // Calls test(E.bar)
}

E foo = E.bar; // This is very bad. A global variable foo hides
               // E.foo from being implicitly globally visible
               // (where applicaple)


On Saturday, 29 September 2012 at 07:04:19 UTC, kenji hara wrote:
>
> It seems to me the root problem is that using a raw-identifier for the start of the inference.
> If there is a symbol literal, the problem may be solved.
>
>   test('bar);  // bar is a symbol, so it does not refer any normal declarations

That would work. I'd be fine with this kind of wild-card enum literal.
September 29, 2012
But, if we were allowed to make a breaking change, then this is how I think it should work:

// in a module scope...

enum E { foo, bar };

-------------------------------------------------------------

// These cause a compile error "foo is ambiguous":
1) E foo = E.bar;
2) E foo = E.foo;
3) enum foo = E.bar;
4) immutable foo = E.bar;
5) immutable foo = initFoo(); // if we can't CTFE initFoo()

// These are fine:
1) enum foo = E.foo;
2) immutable foo = E.foo;
3) int foo = 42; // int isn't implicitly convertible to E
4) E Foo = E.bar;

-------------------------------------------------------------

struct Convertible
{
    E _value;

    alias _value this;
}

Convertible foo; // Compile error: foo is ambiguous

-------------------------------------------------------------

struct MyStruct(T)
{
    T foo = 123; // Fine, hides E.foo inside MyStruct scope

    void fun()
    {
        T foo = 42 // Fine, hides both MyStruct.foo and
                   // E.foo inside this function scope
    }
}

Here's the logic of it:
-----------------------
An enumeration of type E should be visible (like it was a module scope variable) if, and only if, it occurs in one of these "hot-spots" where a value of type E is expected (e.g. it could be in a template parameter list, function argument list, or in a case expression of a switch).

Inside these "hot-spots" the enumeration fights over visibility against variables. It loses that fight against function local and class/struct local variables which have the same name (and whatever type). But it wins the battle over visibility against module scope variables which have the same name but are not implicitly convertible to type E. Against global variables which are of type E or are implicitly convertible to type E, the battle over visibility ends in a tie, and that's a compile-time ambiguity error.
September 29, 2012
On 9/29/12 2:44 PM, Tommi wrote:
> But, if we were allowed to make a breaking change

I stopped reading here :o).

Andrei

September 29, 2012
Scratch my previous post. It had a weird rule where the types of identifiers had a say in whether or not there's ambiguity in the name lookup. That's just silly. It should always be an ambiguity error if the names are identical.

This new rule is easier to conceptualize too. Basically you just think that there are these "hot-spots" (they're red, I think) in your code wherever named enum values are expected. Inside each "hot-spot", the name lookup is allowed to think that all the enumerations (the enumerated identifiers) of that particular named enum type are in module scope. And that's it.

So, again... if we were allowed to make a breaking change, this is how I think it should work:

// in a module scope...

enum Char { a, b, c, d, e }

Char a = Char.c; // OK
auto b = a;      // OK: .b == Char.c
Char c = Char.a; // OK: .c == Char.a

Char d = a; // ERROR: 'a' could be either 'Char.a' or '.a'
            // because now 'a' is in a "hot-spot", where a
            // value convertible to type Char is expected,
            // and thus all Char enumerations can be seen
            // as if they were in module scope

int e = 42; // OK

void test(Char ch) {}

struct MyStruct
{
    int a = 1; // OK: hides '.a'

    void fun()
    {
        Char a = Char.e; // OK: hides 'MyStruct.a' and '.a'

        test(a); // OK: calls 'test(Char.e)' although the test
                 // argument 'a' is now in a "hot-spot" where
                 // all the enumerations of Char (a, b, c, d, e)
                 // are considered to be in module scope. But
                 // the function local 'a' hides anything that
                 // might be in module scope, so the name lookup
                 // doesn't even bother looking at module scope.

        test(e); // ERROR: 'e' could be either 'Char.e' or '.e'
                 // because now the name lookup has to look at
                 // the module scope, where we have also Char.e
                 // visible due to the fact that argument 'e'
                 // is in a "hot-spot". It doesn't matter from
                 // our name-lookup's point of view that test
                 // is not callable with an int value.
    }
}

September 29, 2012
Although it's not very obvious what is a "hot-spot" and what is not.

enum Char { a, b, c, d }

Char a = c; // OK: 'c' is in a "hot-spot"

Char b = c + 1; // ERROR: 'c' is undefined, because it's not in
                // a "hot-spot" and therefore Char enumerations
                // aren't visible. The expression c + 1 is
                // expected to return Char but there's no reason
                // to expect the arguments of that expression to
                // be of type Char. (Another error would be that
                // c + 1 doesn't even return Char, but we never
                // get that far because the name lookup fails)

int c = 42;

void test(Char ch) {}
void test(int val) {}

void main()
{
    test(c); // OK: calls test(.c) because arg 'c' is not in a
             // "hot-spot". Function 'test' isn't expecting a
             // Char variable as an argument, it's expecting a
             // type chosen from set of types (among which Char
             // just so happens to be). But, if you remove the
             // test(int) specialization, this function call
             // fails, and the one on the next line succeeds.

    test(d); // ERROR: 'd' is undefined, because it's not in a
             // "hot-spot" for the reason specified above, and
             // therefore enumerations of Char are not brought
             // into the module scope.
}

It is quite a mess. I think I'm ready to admit that this is not a feature we'd like to have in this language (probably not in any language for that matter).

September 29, 2012
On Saturday, 29 September 2012 at 02:57:42 UTC, David Piepgrass wrote:
>> I have a feature request: "Named enum scope inference"
>>
>> The idea is, that whenever a named enum value is expected, you don't need to explicitly specify the scope of the enum value. This would reduce redundancy in typing, just like automatic type inference does.
>>
>> Examples:
>> ---------
>>
>> enum MyDirection { forward, reverse }
>> struct MyIterator(MyDirection dir)
>> {
>>    ...
>> }
>>
>> int forward = 42; // Doesn't interfere with the next line...
>> auto itr = MyIterator!forward(); // Infers MyDirection.forward
>
> I like the spirit of this feature, but as Alex pointed out, ambiguity is possible (which could theoretically cause errors in existing code) and while I'm not familiar with how the compiler is implemented, my spidey-sense thinks that what you're asking for could be tricky to implement (in a language that already has a very large amount of rules and features.) Plus, I don't like the fact that when you see something like "MyIterator!forward" by itself in code, there is no obvious clue that forward is an enum value and not a class name or a variable. So there is a sort of decrease in clarity of the entire language by increasing the total number of possible meanings that an identifier can have.
>
> So I think this feature would need a more clear syntax, something to indicate that the value is an enum value. I don't currently have a really good counterproposal though....

+1
September 30, 2012
On Saturday, 29 September 2012 at 23:49:47 UTC, Tommi wrote:
> It is quite a mess. I think I'm ready to admit that this is not a feature we'd like to have in this language (probably not in any language for that matter).

Using "with" should do the trick just fine for the few situations where there's too much redundant typing. I think adding new features needs to take a distant back seat to the far FAR more important issues in need of repair. For example, I cannot run D reliably from C/C++, yet that is one of the advertised features that compelled me to seriously consider D as a viable alternative to C/C++. What about dynamic linking? Nope, not yet. This is basic stuff!

Should anyone really care about reducing the amount of typing you have to do when they can barely even use the language as it currently stands?

There are however seemingly trivial items that probably should be added to D, but not as a convenience, instead to make it truely practical in a production environment.

Now having said the above, the last thing D should become is stagnant for sake of preserving backwards compatibility. D2 is not D1, and D3 should not have to be D2. Personally, I'm in favor of the idea of breaking existing code so that past mistakes can be repaired and significant improvements can be implemented, but that should mean moving on to the next major version.

--rt
October 01, 2012
Le 29/09/2012 14:04, Bernard Helyer a écrit :
> Yeah, to respond to the larger topic, the with statement
> is more than enough here. I'm not convinced that complicating
> lookup rules further is worth it.
>

Well, they are not complicated, they are mostly undefined.
1 2 3
Next ›   Last »