Jump to page: 1 2
Thread overview
Feature request: enum init shouldn't create a new enumeration
Oct 13, 2012
Tommi
Oct 13, 2012
denizzzka
Oct 13, 2012
bearophile
Oct 13, 2012
Ali Çehreli
Oct 13, 2012
Jonathan M Davis
Oct 14, 2012
Tommi
Oct 14, 2012
Jonathan M Davis
Oct 14, 2012
Tommi
Oct 14, 2012
Nick Sabalausky
Oct 15, 2012
Tommi
Oct 15, 2012
Tommi
Oct 17, 2012
Daniel Murphy
October 13, 2012
I'd like to be able to specify a default value for a named enum, E.init, without creating a new enumeration. There are three reasons:
1) Default initializing enum variables to an "invalid" value
2) Being able to use 'final switch' without the 'init' case
3) "Invalid" init value wouldn't affect E.min or E.max

Here's what currently happens:

enum MyEnum
{
    init = -123,
    first = 0,
    second = 1
}

void main()
{
    static assert(MyEnum.min == -123);

    MyEnum me;

    final switch (me)
    {
    case MyEnum.first:  break;
    case MyEnum.second: break;
    case MyEnum.init: // I'm forced to specify init case too
    }
}

This is what I'd like to happen:

enum MyEnum
{
    init = -123,
    first = 0,
    second = 1
}

void main()
{
    static assert(MyEnum.min == 0); // no effect on min/max

    MyEnum me;

    final switch (me) // no init case necessary nor allowed
    {
    case MyEnum.first:  break;
    case MyEnum.second: break;
    }
}
October 13, 2012
On Saturday, 13 October 2012 at 15:39:24 UTC, Tommi wrote:
> enum MyEnum
> {
>     init = -123,
>     first = 0,
>     second = 1
> }
>
> void main()
> {
>     static assert(MyEnum.min == -123);
>
>     MyEnum me;
>
>     final switch (me)
>     {
>     case MyEnum.first:  break;
>     case MyEnum.second: break;
>     case MyEnum.init: // I'm forced to specify init case too
>     }
> }


Also, a quick question:

Why in case its need to write name of the enum?

     case first:  break;
     case second: break;
     case init: // I'm forced to specify init case too

looks better for me.
October 13, 2012
denizzzka:

> Why in case its need to write name of the enum?

Because D enums have a very simple design. But with() helps.

Bye,
bearophile
October 13, 2012
On 10/13/2012 11:01 AM, bearophile wrote:
> denizzzka:
>
>> Why in case its need to write name of the enum?
>
> Because D enums have a very simple design. But with() helps.

For me, that is the only benefit of 'with':

    final switch (me) with (MyEnum)
    {
    case first:  break;
    case second: break;
    case init: break;
    }

The other uses of 'with' are more like obfuscations.

>
> Bye,
> bearophile

Ali
October 13, 2012
On Saturday, October 13, 2012 17:39:23 Tommi wrote:
> I'd like to be able to specify a default value for a named enum,
> E.init, without creating a new enumeration. There are three
> reasons:
> 1) Default initializing enum variables to an "invalid" value
> 2) Being able to use 'final switch' without the 'init' case
> 3) "Invalid" init value wouldn't affect E.min or E.max
> 
> Here's what currently happens:
> 
> enum MyEnum
> {
>      init = -123,
>      first = 0,
>      second = 1
> }
> 
> void main()
> {
>      static assert(MyEnum.min == -123);
> 
>      MyEnum me;
> 
>      final switch (me)
>      {
>      case MyEnum.first:  break;
>      case MyEnum.second: break;
>      case MyEnum.init: // I'm forced to specify init case too
>      }
> }
> 
> This is what I'd like to happen:
> 
> enum MyEnum
> {
>      init = -123,
>      first = 0,
>      second = 1
> }
> 
> void main()
> {
>      static assert(MyEnum.min == 0); // no effect on min/max
> 
>      MyEnum me;
> 
>      final switch (me) // no init case necessary nor allowed
>      {
>      case MyEnum.first:  break;
>      case MyEnum.second: break;
>      }
> }

Think about that for a moment. What happens when that final switch statement is actually run? Which statement would MyEnum.init use? The whole point of final switch is that the compile _knows_ that every single value for that type has a case. With your suggestion, it specifically _doesn't_ have a case for one of the type's values. And it _will_ happen at some point that you'll hit a switch like that with the init value rather than a valid one. I don't see how that can possibly work or make any sense at all.

And remember, that in many cases, T.init is considered to be perfectly valid. By allowing a final switch _not_ to have it, it then becomes easy to forget to add a case for it when you _need_ to, completely defeating the purpose of the final switch (to make it so that both you and the compiler know that all of the possible values are accounted for).

- Jonathan M Davis
October 14, 2012
On Saturday, 13 October 2012 at 20:25:56 UTC, Jonathan M Davis wrote:
>> 
>>      MyEnum me;
>> 
>>      final switch (me) // no init case necessary nor allowed
>>      {
>>      case MyEnum.first:  break;
>>      case MyEnum.second: break;
>>      }
>> }
>
> Think about that for a moment. What happens when that final switch statement is actually run?

There's a bug in that code, because MyEnum default-initializes to an invalid value. It's effectively the same as this following code, where the programmer has failed to initialize MyEnum variable with a valid value:

enum MyEnum { first, second }

void main()
{
    MyEnum me = cast(MyEnum)(-123);
	
    final switch (me)
    {
    case MyEnum.first:  break;
    case MyEnum.second: break;
    }
}

I think that the final switch statement above should throw an unrecoverable error. I tested it, and nothing happens. I strongly disagree with this behavior of the compiler (a bug perhaps?).

> And remember, that in many cases, T.init is considered to be perfectly valid. By allowing a final switch _not_ to have it,
> it then becomes easy to forget to add a case for it when you
> _need_ to, completely defeating the purpose of the final
> switch (to make it so that both you and the compiler know that all of the possible values are accounted for).

If T.init is considered to be perfectly valid, then it means, that a synonym for it exists among the enumerations. E.g:

enum MyEnum  { init = 1, first = 1, second = 42 }
enum ThyEnum { first, second }

In both of those cases, T.first is the synonym of T.init, which is what both of those enums default-initialize to. Therefore, if T.init is a valid value, and thus has a synonym among the enumerations of T, then you can't add a case for init in a final switch:

MyEnum me;

final switch (me)
{
    MyEnum.first:  break;
    MyEnum.second: break;
    MyEnum.init:   // Error: duplicate case cast(MyEnum)1
                   // in switch statement
}

So, this situation you describe, where you *need* to add a case for init in a final switch, it doesn't exist. T.init should always either 1) have a synonym among the enumerations or 2) represent an invalid value.
October 14, 2012
On Sunday, October 14, 2012 08:20:40 Tommi wrote:
> There's a bug in that code, because MyEnum default-initializes to an invalid value. It's effectively the same as this following code, where the programmer has failed to initialize MyEnum variable with a valid value:

Valid or not, MyEnum.init is still a value for MyEnum and _must_ be accounted for. Trying to treat MyEnum.init as not being a value of MyEnum is likely to break all kinds of stuff. Being able to have a variable of an enum type with a value which is not really a member of the enum is just plain broken.

And honestly, declaring a specific init value for an enum is a stupid idea. It's going to screw with all kinds of stuff. Anything assuming that the init property is the first value (is it is in _all_ other cases but isn't necessarily if you declare your own) will be broken. It's still going to end up being in stuff like std.traits.EnumMembers or pretty much anything which operates on enums unless all kinds of special casing is added. TDPL does mention (p. 275) that you can declare enum members with the names max, min, and init, but it also points out that it's a dumb idea. I'd argue that it shouldn't even be legal at all. It's just begging for trouble.

- Jonathan M Davis
October 14, 2012
On Sunday, 14 October 2012 at 06:51:48 UTC, Jonathan M Davis wrote:
> And honestly, declaring a specific init value for an enum is a stupid idea.

I think that declaring a specific *valid* init value for an enum has no purpose. But declaring a specific *invalid* init value, to which the enum initializes to by default, is a very good idea. The reason for why it's a good idea, is exactly the same as why default-initializing floating point variables to NaN is a good idea. Others have reasoned about that enough, thus I don't have to.

On Sunday, 14 October 2012 at 06:51:48 UTC, Jonathan M Davis wrote:
> ... It's going to screw with all kinds of stuff.

True, it would break code.



October 14, 2012
On Sun, 14 Oct 2012 09:16:28 +0200
"Tommi" <tommitissari@hotmail.com> wrote:

> On Sunday, 14 October 2012 at 06:51:48 UTC, Jonathan M Davis wrote:
> > And honestly, declaring a specific init value for an enum is a stupid idea.
> 
> I think that declaring a specific *valid* init value for an enum has no purpose. But declaring a specific *invalid* init value, to which the enum initializes to by default, is a very good idea. The reason for why it's a good idea, is exactly the same as why default-initializing floating point variables to NaN is a good idea. Others have reasoned about that enough, thus I don't have to.
> 

Yes, but it still has to be taken into account in things like "final switch". You can't just pretend that nothing will ever have that value, because by making it the init value, you've *made* it an actual possible value, one that just happens to indicate "uninitialized".

October 15, 2012
On Sunday, 14 October 2012 at 19:40:17 UTC, Nick Sabalausky wrote:
> Yes, but it still has to be taken into account in things like "final switch". You can't just pretend that nothing will ever
> have that value, because by making it the init value, you've
> *made* it an actual possible value, one that just happens to
> indicate "uninitialized".

*I* know that named enum variables can have whatever values; it's rather *you* and DMD who are pretending that enum variables can have only those values which are specifically enumerated.

You say that final switch should take into account an enum init value which can be used to represent an invalid value. I say that final switch shouldn't consider that (invalid) init value any different from all the other values that are invalid for that specific enum type, that is: all the values that are not speficied explicitly by the enumerations. And the way final switch should take all those invalid values into account, is by throwing an error when a final switch switches on a value not specifically defined by the enumerations.

Dmd also seems blinded into thinking that the specified enumerations are all that an enum variable can ever be, while in reality, it's very easy to write a bug that makes an enum variable have an invalid value. E.g:

enum MyEnum { first, second, third }

auto me = MyEnum.min;

while (me < MyEnum.max)
{
    // do something
    ++me;
}

switch (me) // this should throw
{
case MyEnum.first:  break;
case MyEnum.second: break;
case MyEnum.thrird: break;
}
« First   ‹ Prev
1 2