Jump to page: 1 2 3
Thread overview
I have a feature request: "Named enum scope inference"
Sep 29, 2012
Tommi
Sep 29, 2012
Tommi
Sep 29, 2012
Tommi
Sep 29, 2012
David Piepgrass
Sep 29, 2012
Andrej Mitrovic
Sep 29, 2012
Ben Davis
Sep 29, 2012
Tommi
Sep 29, 2012
Tommi
Sep 29, 2012
Tommi
Sep 29, 2012
Bernard Helyer
Sep 29, 2012
Tommi
Sep 29, 2012
kenji hara
Sep 29, 2012
Tommi
Sep 29, 2012
Tommi
Sep 29, 2012
Tommi
Sep 29, 2012
Tommi
Sep 30, 2012
Rob T
Sep 29, 2012
Tommi
Sep 29, 2012
Mehrdad
Sep 29, 2012
Jonathan M Davis
Sep 29, 2012
Andrej Mitrovic
Sep 29, 2012
Iain Buclaw
Sep 29, 2012
Bernard Helyer
Oct 01, 2012
deadalnix
September 29, 2012
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

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

enum MyLockType { read, read_write }

struct MyScopedLock
{
    this(MyMutex mutex, MyLockType lockType)
    {
        ...
    }
}

shared MyMutex g_mutex;
...
auto scopedLock = MyScopedLock(g_mutex, read_write);
// Infered MyLockType.read_write

// Side note: Compare the above to having a boolean flag...
auto scopedLock = MyInferiorScopedLock(g_mutex, true);
// ... and you have to read the docs to know what 'true' means

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

enum MyFruit { apple, orange, banana }

MyFruit fruit;

switch (fruit)
{
    case apple:  break; // Case expressions know what type to
    case orange: break; // expect based on the switch expression
    case banana: break;
}



September 29, 2012
On 29-09-2012 03:55, Tommi 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
>
> ----------------------------------------------------------------
>
> enum MyLockType { read, read_write }
>
> struct MyScopedLock
> {
>      this(MyMutex mutex, MyLockType lockType)
>      {
>          ...
>      }
> }
>
> shared MyMutex g_mutex;
> ...
> auto scopedLock = MyScopedLock(g_mutex, read_write);
> // Infered MyLockType.read_write
>
> // Side note: Compare the above to having a boolean flag...
> auto scopedLock = MyInferiorScopedLock(g_mutex, true);
> // ... and you have to read the docs to know what 'true' means
>
> ----------------------------------------------------------------
>
> enum MyFruit { apple, orange, banana }
>
> MyFruit fruit;
>
> switch (fruit)
> {
>      case apple:  break; // Case expressions know what type to
>      case orange: break; // expect based on the switch expression
>      case banana: break;
> }
>
>
>

The first issue with this proposal that comes to mind is this:

enum Foo { bar }

void func(Foo f)
{
    // ...
}

// ...

Foo bar = Foo.bar;
func(bar); // ?

-- 
Alex Rønne Petersen
alex@lycus.org
http://lycus.org
September 29, 2012
On Saturday, 29 September 2012 at 02:01:15 UTC, Alex Rønne Petersen wrote:
> The first issue with this proposal that comes to mind is this:
>
> enum Foo { bar }
>
> void func(Foo f)
> {
>     // ...
> }
>
> // ...
>
> Foo bar = Foo.bar;
> func(bar); // ?

Maybe it should simply throw a compile error about the ambiguity of 'bar' in 'func(bar)'. But if the local variable 'bar' if not of Foo type, then there's no ambiguity. Or, if the local variable 'bar' is a compile time constant that evaluates to Foo.bar, then there's no ambiguity either.
September 29, 2012
On 29-09-2012 04:31, Tommi wrote:
> On Saturday, 29 September 2012 at 02:01:15 UTC, Alex Rønne Petersen wrote:
>> The first issue with this proposal that comes to mind is this:
>>
>> enum Foo { bar }
>>
>> void func(Foo f)
>> {
>>     // ...
>> }
>>
>> // ...
>>
>> Foo bar = Foo.bar;
>> func(bar); // ?
>
> Maybe it should simply throw a compile error about the ambiguity of
> 'bar' in 'func(bar)'. But if the local variable 'bar' if not of Foo
> type, then there's no ambiguity. Or, if the local variable 'bar' is a
> compile time constant that evaluates to Foo.bar, then there's no
> ambiguity either.

Regardless of the conditions under which to throw an error, it would be a breaking change.

-- 
Alex Rønne Petersen
alex@lycus.org
http://lycus.org
September 29, 2012
> 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....
September 29, 2012
On Saturday, September 29, 2012 03:55:48 Tommi wrote:
> enum MyFruit { apple, orange, banana }
> 
> MyFruit fruit;
> 
> switch (fruit)
> {
>      case apple:  break; // Case expressions know what type to
>      case orange: break; // expect based on the switch expression
>      case banana: break;
> }

This could be achieved by simply making it so that when a variable of an enum type is used in a switch statement, the cases permit you to omit the enum's type when referring to the enum values. None of the name inferrence stuff that you're suggesting would be required for that. Though at the moment, I believe that you could simply use with to solve the problem:

with(MyFruit)
{
    switch(fruit)
    {
        case apple: break;
        case orange: break;
        case banana: break;
    }
}

All that would be required to do it without the with would be to make it so that the compiler implicitly added the with when an enum type is used in the switch.

- Jonathan M Davis
September 29, 2012
On 9/29/12, David Piepgrass <qwertie256@gmail.com> wrote:
> I like the spirit of this feature, but as Alex pointed out, ambiguity is possible (which could theoretically cause errors in existing code)

It could also cause subtle problems because enum values are implicitly convertible to the enum's base type. Take this for example:

void test(bool state) { }
enum Foo { no, yes }
class Class
{
    enum Bar { yes, no }
    void test() { .test(no); }  // pass Foo.no (true) or Bar.no (false) ?
}
September 29, 2012
On 9/29/12, Jonathan M Davis <jmdavisProg@gmx.com> wrote:
> you could simply use with to solve the problem:
>
> with(MyFruit)
> {
>     switch(fruit)
>     {
>         case apple: break;
>         case orange: break;
>         case banana: break;
>     }
> }
>

It's even simpler:
switch(fruit) with (MyFruit)
{
    case apple: break;
    case orange: break;
    case banana: break;
}
September 29, 2012
On Saturday, 29 September 2012 at 02:37:40 UTC, Alex Rønne Petersen wrote:
>
> Regardless of the conditions under which to throw an error, it would be a breaking change.

I guess that's a bad thing. Hmmm... too bad.

Well... maybe we could make it so that variables of the requested enum type are looked up first, and if such is found (even with a same name as one of the enumerations), then that variable is used. Only if no variables of that name exist, then you see if the name correctly maps to a named enum literal, and if not then you see if you can map the name using the enum type name as its scope. I think that's quite a logical rule anyway, because if you've defined a variable of the correct enum type and pass it to a function, you quite likely meant to pass that variable to that function (instead of passing an enum literal).
September 29, 2012
On Saturday, 29 September 2012 at 02:57:42 UTC, David Piepgrass wrote:
>
> 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.

But, if you use the explicit "MyIterator!(MyDirection.forward)" in your code, I don't think that's any more clear about what "forward" actually is. It could be anything:

struct MyDirection
{
    struct forward {}
    // ... or:
    static @property int forward() { return 42; }
    // ... or:
    static enum forward = 1.5;
}

Only extra clue that get with the the explicit form (EnumType.enumerationName) is the name of the enum type. If it makes the code clearer, then use explicit form. But more often than not, the combination of the enumeration name and the context where it is used makes the intention clear enough.


On 9/29/12, David Piepgrass <qwertie256@gmail.com> wrote:
> 
> It could also cause subtle problems because enum values are implicitly
> convertible to the enum's base type. Take this for example:
> 
> void test(bool state) { }
> enum Foo { no, yes }
> class Class
> {
>    enum Bar { yes, no }
>    void test() { .test(no); }  // pass Foo.no (true) or Bar.no (false) ?
> }

But that's not what I'm suggesting. The feature suggested is:
"Try to perform implicit scoping (as a last resort), if a named enum variable is expected". Your function "void test(bool state)" doesn't *expect* a named enum as an argument. If it did expect, say Foo, as an argument, then Foo.no would be passed.
« First   ‹ Prev
1 2 3