April 04, 2014
On Friday, 4 April 2014 at 18:15:43 UTC, Meta wrote:
>> And another:
>>
>>  array[A + 1] = t; // Error: incompatible types Index and int
>>
>> And another:
>>
>>  enum Mask { A=1,B=4 }
>>
>>  Mask m = A | B;   // Error: incompatible operator | for enum
>>
>> and on it goes. These are routine and normal uses of enums.

https://github.com/D-Programming-Language/phobos/pull/2058

Perhaps we *will* get typesafe enums of a sort via a library implementation.
April 04, 2014
Walter Bright:

Thank you for the answers.

> Here's one:
>
>   enum Index { A, B, C }
>   T[Index.max] array; // Error: Index.max is not an int
>   ...
>   array[B] = t;   // Error: B is not an int

In the last months I've grown a moderate desire for optionally strongly typed array indexes in D (as seen in Ada, but with a different syntax) (it's optional, so it's meant to be an additive change, that causes no harm to existing D code). With them code like yours becomes OK (as it's OK in Ada). Such optional strong typing for array indexes is not means for script-like D programs, but for the medium-integrity D programs.


> And another:
>
>   array[A + 1] = t; // Error: incompatible types Index and int

This can be solved with optionally strongly typed array indexes plus a succ/prec property for enums. I have asked for such property years ago. In Ada you use the built in function "Succ". Alternatively, in D you can also use a library-defined group of little functions/templates succ/prec/Succ/Prec (that contain a cast, but it's in Phobos, so it's less dangerous than a cast in user code):

array[Succ!(Index.A)] = t;
auto i = Index.A;
array[i.succ] = t;


> And another:
>
>   enum Mask { A=1,B=4 }
>
>   Mask m = A | B;   // Error: incompatible operator | for enum

In GitHub there is a patch that is meant to implement Flags in library code (in C# such Flags are almost first-class, using [Flags]):

https://github.com/D-Programming-Language/phobos/pull/2058

If such Flags is implemented with enums, then it contains casts, but again casts in Phobos are less dangerous than casts in user code.

Bye,
bearophile
April 04, 2014
Meta:

> Also, if I remember correctly, min and max are terribly
> broken for enums in the first place.

Yes, perhaps they need to be deprecated.

Also, there is a ugly name clashing between enum field names and the enum properties. The solution is to group them into a single namespace (like "meta"), and then forbid an enum member with the name of the namespace.

https://d.puremagic.com/issues/show_bug.cgi?id=4997

Unfortunately overall the design of D enums has more holes than swiss cheese. This is why in a recent post I said to Andrei that perhaps there are still several little breaking changes to do to D, and they need priority over additive enhancements.

Bye,
bearophile
April 04, 2014
>> And another:
>>
>>  enum Mask { A=1,B=4 }
>>
>>  Mask m = A | B;   // Error: incompatible operator | for enum

That would be a 'set of enum' in Pascal/Delphi.
April 04, 2014
On Fri, Apr 04, 2014 at 11:02:01 -0700, Walter Bright wrote:
> Most of the casts in Warp come from the workarounds I had to do to get around the auto-decode of std.array.front(). I have designed byChar, byWchar and byDchar ranges for Phobos to get around this issue, but that is stalled now because of the messed up design of ranges.

Sorry, I'm a D noob; what's 'auto-decode'?

> Here's one:
> 
>   enum Index { A, B, C }
>   T[Index.max] array; // Error: Index.max is not an int
>   ...
>   array[B] = t;   // Error: B is not an int

Maybe instead of having array indices be int, having them specify some interface (akin to Ix[1] used by Haskell's Array). Not that this is likely fixable at this point.

> And another:
> 
>   array[A + 1] = t; // Error: incompatible types Index and int
> 
> And another:
> 
>   enum Mask { A=1,B=4 }
> 
>   Mask m = A | B;   // Error: incompatible operator | for enum

I like Qt's Q_FLAG and Q_FLAGS where you have a separate type for the flags and combined flags. Maybe something like:

    enum MaskBits { mixin EnumBits!MaskBits; A=1, B=4 }
    alias Flags!MaskBits Mask;

where EnumBits would define the binary operations would be possible?

> And besides, even if such strongly typed enums were a good idea, making such a change would be an utter disaster for existing code. It is out of the question.

Agreed.

There's also Haskell's 'newtype' which might be useful to have (strongalias? strictalias?). I guess this is no different than something like:

    class NewType(T) {
        private:
            T store;

            public this(T store) {
                this.store = store;
            }

            package T unT() {
                return store;
            }
    }

and if you want unT to be public:

    public T unT(NewType!T nt) {
        return nt.unT();
    }

--Ben

[1]http://www.haskell.org/ghc/docs/latest/html/libraries/base/Data-Ix.html
April 04, 2014
> array[Succ!(Index.A)] = t;
> auto i = Index.A;
> array[i.succ] = t;

And with "enum precondition" in the succ() function you can do both cases with a single function:

array[Index.A.succ] = t;
auto i = Index.A;
array[i.succ] = t;

Bye,
bearophile
April 04, 2014
On 4/4/2014 11:47 AM, bearophile wrote:
> Also, there is a ugly name clashing between enum field names and the enum
> properties. The solution is to group them into a single namespace (like "meta"),
> and then forbid an enum member with the name of the namespace.
>
> https://d.puremagic.com/issues/show_bug.cgi?id=4997

Actually, that was intentional, which is why the issue is marked as "enhancement". The builtin properties are override-able.


> Unfortunately overall the design of D enums has more holes than swiss cheese.

Enums are not meant to be rigidly typed.


> This is why in a recent post I said to Andrei that perhaps there are still
> several little breaking changes to do to D, and they need priority over additive
> enhancements.

I understand your concerns, but I don't share your opinion that they need fixing. Their behaviors were deliberately designed, and in my experience work out nicely.
April 04, 2014
On 4/4/2014 12:05 PM, bearophile wrote:
> And with "enum precondition" in the succ() function you can do both cases with a
> single function:
>
> array[Index.A.succ] = t;
> auto i = Index.A;
> array[i.succ] = t;

What about i+10? Do you expect the person to write i.succ.succ.succ.succ.succ.succ.succ.succ.succ.succ? Sorry, that sux!

And what about:

    int j;
    array[i+j]

?

And forcing the user to use templates to do any logical or arithmetic operations on enum operands? It's just awful.




April 04, 2014
On Friday, 4 April 2014 at 18:57:44 UTC, Ben Boeckel wrote:
> On Fri, Apr 04, 2014 at 11:02:01 -0700, Walter Bright wrote:
>> Most of the casts in Warp come from the workarounds I had to do to
>> get around the auto-decode of std.array.front(). I have designed
>> byChar, byWchar and byDchar ranges for Phobos to get around this
>> issue, but that is stalled now because of the messed up design of
>> ranges.
>
> Sorry, I'm a D noob; what's 'auto-decode'?

unicode decoding. front decodes a code-point from a string instead of a code-unit (single char)
April 04, 2014
On Fri, Apr 04, 2014 at 13:04:28 -0700, Walter Bright wrote:
> On 4/4/2014 12:05 PM, bearophile wrote:
> >And with "enum precondition" in the succ() function you can do both cases with a
> >single function:
> >
> >array[Index.A.succ] = t;
> >auto i = Index.A;
> >array[i.succ] = t;
> 
> What about i+10? Do you expect the person to write i.succ.succ.succ.succ.succ.succ.succ.succ.succ.succ? Sorry, that sux!

You say that, I see a pattern:

Presumably, you'd have something like:

    iter :: Int -> (a -> a) -> a -> a

so you'd have:

    array[iter(j, succ, i)]

But really, I think using an interface (cf Ix referenced elsewhere in the thread) rather than a concrete type for array indices may have been better, but I also think that ship has sailed here.

> And what about:
> 
>     int j;
>     array[i+j]

Why would you be adding arbitrary integers to enumerations and expecting a valid result?

> And forcing the user to use templates to do any logical or arithmetic operations on enum operands? It's just awful.

Again, interfaces :) .

--Ben