I'm bringing this over here from a couple separate threads over on "D.learn" (My "D1: Overloading across modules" and bearophile's "Enum equality test").
Background summary:
bearophile:
> I'm looking for D2 rough edges. I've found that this D2 code compiles and doesn't assert at runtime:
>> enum Foo { V1 = 10 }
> void main() {
> assert(Foo.V1 == 10);
> }
>> But I think enums and integers are not the same type,
> and I don't want to see D code that hard-codes comparisons
> between enum instances and number literals, so I think an
> equal between an enum and an int has to require a cast:
>> assert(cast(int)(Foo.V1) == 10); // OK
He goes on to mention C++0x's "enum class" that, smartly, gets rid of that implicit conversion nonsense.
To put it simply, I agree with this even on mere principle. I'm convinced that the current D behavior is a blatant violation of strong-typing and smacks way too much of C's so-called "type system".
But here's another reason to get rid it that I, quite coincidentally, stumbled upon right about the same time:
Me:
> In D1, is there any reason I should be getting an error on this?:
>> // module A:
> enum FooA { fooA };
> void bar(FooA x) {}
>> // module B:
> import A;
> enum FooB { fooB };
> void bar(FooB x) {}
>> bar(FooB.fooB); // Error: A.bar conflicts with B.bar (WTF?)
In the resulting discussion (which included a really hackish workaround), it was said that this is because of a rule (that I assume exists in D2 as well) that basically goes "two functions from different modules are in conflict if they have the same name." I assume (and very much hope) that the rule also has a qualification "...but only if implicit conversion rules make it possible for one to hijack the other".
It was said that this is to prevent a function call from getting hijacked by merely importing a module (or making a change in an imported module). That I can completely agree with. But I couldn't understand why this would cause conflicts involving enums until I thought about implicit enum-to-base-type conversion and came up with this scenario:
// Module Foo:
enum Foo { foo }
// module A:
import Foo;
void bar(Foo x){}
// module B version 1:
import Foo; // Note: A is not imported yet
void bar(int x){}
bar(Foo.foo); // Stupid crap that should never be allowed in the first place
// module B version 2:
import Foo;
import A; // <- This line added
void bar(int x){}
bar(Foo.foo); // Now that conflict error *cough* "helps".
So thanks to the useless and dangerous ability to implicitly convert an enum to its base type, we can't have certain perfectly sensible cross-module overloads.
Although, frankly, I *still* don't see why "bar(SomeEnum)" and "bar(SomeOtherEnum)" should ever be in conflict (unless that's only D1, or if implicit base-type-to-enum conversions are allowed (which would make things even worse)).

Nick Sabalausky Wrote:
> I'm bringing this over here from a couple separate threads over on "D.learn" (My "D1: Overloading across modules" and bearophile's "Enum equality test").
> > Background summary:
> > bearophile:
> > I'm looking for D2 rough edges. I've found that this D2 code compiles and doesn't assert at runtime:
> >> > enum Foo { V1 = 10 }
> > void main() {
> > assert(Foo.V1 == 10);
> > }
> >> > But I think enums and integers are not the same type,
> > and I don't want to see D code that hard-codes comparisons
> > between enum instances and number literals, so I think an
> > equal between an enum and an int has to require a cast:
> >> > assert(cast(int)(Foo.V1) == 10); // OK
> > He goes on to mention C++0x's "enum class" that, smartly, gets rid of that implicit conversion nonsense.
> > To put it simply, I agree with this even on mere principle. I'm convinced that the current D behavior is a blatant violation of strong-typing and smacks way too much of C's so-called "type system".
> > But here's another reason to get rid it that I, quite coincidentally, stumbled upon right about the same time:
> > Me:
> > In D1, is there any reason I should be getting an error on this?:
> >> > // module A:
> > enum FooA { fooA };
> > void bar(FooA x) {}
> >> > // module B:
> > import A;
> > enum FooB { fooB };
> > void bar(FooB x) {}
> >> > bar(FooB.fooB); // Error: A.bar conflicts with B.bar (WTF?)
> > In the resulting discussion (which included a really hackish workaround), it was said that this is because of a rule (that I assume exists in D2 as well) that basically goes "two functions from different modules are in conflict if they have the same name." I assume (and very much hope) that the rule also has a qualification "...but only if implicit conversion rules make it possible for one to hijack the other".
> > It was said that this is to prevent a function call from getting hijacked by merely importing a module (or making a change in an imported module). That I can completely agree with. But I couldn't understand why this would cause conflicts involving enums until I thought about implicit enum-to-base-type conversion and came up with this scenario:
> > // Module Foo:
> enum Foo { foo }
> > // module A:
> import Foo;
> void bar(Foo x){}
> > // module B version 1:
> import Foo; // Note: A is not imported yet
> void bar(int x){}
> bar(Foo.foo); // Stupid crap that should never be allowed in the first place
> > // module B version 2:
> import Foo;
> import A; // <- This line added
> void bar(int x){}
> bar(Foo.foo); // Now that conflict error *cough* "helps".
> > So thanks to the useless and dangerous ability to implicitly convert an enum to its base type, we can't have certain perfectly sensible cross-module overloads.
> > Although, frankly, I *still* don't see why "bar(SomeEnum)" and "bar(SomeOtherEnum)" should ever be in conflict (unless that's only D1, or if implicit base-type-to-enum conversions are allowed (which would make things even worse)).
> >
Hum...
+1
What can I add, you said it all ;)

Nick Sabalausky Wrote:
> I'm bringing this over here from a couple separate threads over on "D.learn" (My "D1: Overloading across modules" and bearophile's "Enum equality test").
> > Background summary:
> > bearophile:
> > I'm looking for D2 rough edges. I've found that this D2 code compiles and doesn't assert at runtime:
> >> > enum Foo { V1 = 10 }
> > void main() {
> > assert(Foo.V1 == 10);
> > }
> >> > But I think enums and integers are not the same type,
> > and I don't want to see D code that hard-codes comparisons
> > between enum instances and number literals, so I think an
> > equal between an enum and an int has to require a cast:
> >> > assert(cast(int)(Foo.V1) == 10); // OK
> > He goes on to mention C++0x's "enum class" that, smartly, gets rid of that implicit conversion nonsense.
> > To put it simply, I agree with this even on mere principle. I'm convinced that the current D behavior is a blatant violation of strong-typing and smacks way too much of C's so-called "type system".
> > But here's another reason to get rid it that I, quite coincidentally, stumbled upon right about the same time:
> > Me:
> > In D1, is there any reason I should be getting an error on this?:
> >> > // module A:
> > enum FooA { fooA };
> > void bar(FooA x) {}
> >> > // module B:
> > import A;
> > enum FooB { fooB };
> > void bar(FooB x) {}
> >> > bar(FooB.fooB); // Error: A.bar conflicts with B.bar (WTF?)
> > In the resulting discussion (which included a really hackish workaround), it was said that this is because of a rule (that I assume exists in D2 as well) that basically goes "two functions from different modules are in conflict if they have the same name." I assume (and very much hope) that the rule also has a qualification "...but only if implicit conversion rules make it possible for one to hijack the other".
> > It was said that this is to prevent a function call from getting hijacked by merely importing a module (or making a change in an imported module). That I can completely agree with. But I couldn't understand why this would cause conflicts involving enums until I thought about implicit enum-to-base-type conversion and came up with this scenario:
> > // Module Foo:
> enum Foo { foo }
> > // module A:
> import Foo;
> void bar(Foo x){}
> > // module B version 1:
> import Foo; // Note: A is not imported yet
> void bar(int x){}
> bar(Foo.foo); // Stupid crap that should never be allowed in the first place
> > // module B version 2:
> import Foo;
> import A; // <- This line added
> void bar(int x){}
> bar(Foo.foo); // Now that conflict error *cough* "helps".
> > So thanks to the useless and dangerous ability to implicitly convert an enum to its base type, we can't have certain perfectly sensible cross-module overloads.
> > Although, frankly, I *still* don't see why "bar(SomeEnum)" and "bar(SomeOtherEnum)" should ever be in conflict (unless that's only D1, or if implicit base-type-to-enum conversions are allowed (which would make things even worse)).
> >
This also interacts with the crude hack of "this enum is actually a constant".
if you remove the implicit casts than how would you be able to do:
void foo(int p);
enum { bar = 4 }; // don't remember the exact syntax here
foo(bar); // compile-error?!
I feel that enum needs to be re-designed. I think that C style "enums are numbers" are *bad*, *wrong* designs that expose internal implementation and the only valid design is that of Java 5.
e.g.
enum Color {blue, green}
Color c = Color.blue;
c++; // WTF? should NOT compile
A C style enum with values assigned is *not* an enumeration but rather a set of meaningful integral values and should be represented as such.
This was brought up many many times in the NG before and based on past occurences will most likely never change.

yigal chripun:
> This was brought up many many times in the NG before and based on past occurences will most likely never change.
If I see some semantic holes I'd like to see them filled/fixed, when possible. Keeping the muzzle doesn't improve the situation :-)
Bye,
bearophile

yigal chripun Wrote:
> A C style enum with values assigned is *not* an enumeration but rather a set of meaningful integral values and should be represented as such.
>
The above isn't accurate. I'll re-phrase:
The values assigned to the members of the enums are just properties of the members, they do not define their identity.
void bar(int);
bar(Color.Red.rgb); // no-problem
bar(Color.Red); // compile-error

bearophile Wrote:
> yigal chripun:
> > This was brought up many many times in the NG before and based on past occurences will most likely never change.
> > If I see some semantic holes I'd like to see them filled/fixed, when possible. Keeping the muzzle doesn't improve the situation :-)
> > Bye,
> bearophile
I agree with you about the gaping semantic hole. All I'm saying is that after bringing this so many times to discussion before I lost hope that this design choice will ever be re-considered.

"yigal chripun" <yigal100@gmail.com> wrote in message news:hobg4b$12ej$1@digitalmars.com...>> This also interacts with the crude hack of "this enum is actually a
> constant".
> if you remove the implicit casts than how would you be able to do:
> void foo(int p);
> enum { bar = 4 }; // don't remember the exact syntax here
> foo(bar); // compile-error?!
>
AIUI, That style enum is already considered different by the compiler anyway. Specifically, it's doesn't create any new type, whereas the other type of enum creates a new semi-weak type. I don't think it would be too big of a step to go one step further and change "this kind of enum creates a new semi-weak type" to "this kind of enum creates a new strong type". But yea, I absolutely agree that calling a manifest constant an "enum" is absurd. It still bugs the hell out of me even today, but I've largely shut up about it since Walter hasn't wanted to change it even though he seems to be the only one who doesn't feel it's a bad idea (and it's not like it causes practical problems when actually using the language...although I'm sure it must be a big WTF for new and prospective D users).
> I feel that enum needs to be re-designed. I think that C style "enums are numbers" are *bad*, *wrong* designs that expose internal implementation and the only valid design is that of Java 5.
>> e.g.
> enum Color {blue, green}
> Color c = Color.blue;
> c++; // WTF? should NOT compile
>> A C style enum with values assigned is *not* an enumeration but rather a set of meaningful integral values and should be represented as such.
>> This was brought up many many times in the NG before and based on past occurences will most likely never change.
I would hate to see enums lose the concept of *having* a base type and base values because I do find that to be extremely useful (Haxe's enums don't have a base type and, from direct experience with them, I've found that to be a PITA too). But I feel very strongly that conversions both to and from the base type need to be explicit. In fact, that was one of the things that was bugging me about C/C++ even before I came across D. D improves the situation of course, but it's still only half-way.

Nick Sabalausky:
> It still bugs the hell out of me even today, but I've largely shut up about it since Walter hasn't wanted to change it even though he seems to be the only one who doesn't feel it's a bad idea (and it's not like it causes practical problems when actually using the language...although I'm sure it must be a big WTF for new and prospective D users).
Recently D2 has introduced the name "inout", that doesn't seem very linked to its semantic purpose. I think "auto_const", "auto const" or "autoconst" are better.
The recently introduced "auto ref" is clear, but I think "auto_ref" or "autoref"are better still.
Bye,
bearophile

Nick Sabalausky wrote:
> So thanks to the useless and dangerous ability to implicitly convert an enum to its base type, we can't have certain perfectly sensible cross-module overloads.
One thing being able to convert enum to it's base type does allow is this:
import std.stdio;
enum FLAG
{
READ = 0x1,
WRITE = 0x2,
OTHER = 0x4
}
void foo(FLAG flags)
{
writeln("Flags = ", flags);
}
int main(string[] args)
{
foo(FLAG.READ);
foo(FLAG.READ|FLAG.WRITE);
return 0;
}
I find being able to define bit flag values with an enum and combine them using | and |= or negate with &= ~flag etc very useful.
Languages without the implicit conversion typically give an error when using |, |= etc forcing you to cast, eg.
foo((int)FLAG.READ|(int)FLAG.WRITE);
which, in addition, breaks type safety as you're casting to 'int'.
Alternately they require you to define |, |= etc for the 'strong' enum type which is a PITA, IMO.
Granted, that's probably the most 'correct' way for a strongly typed language to do things, but it just feels un-necessary for someone who is used to the convenience of the implicit conversion.
All that said, I also find method/function collision very annoying. True, it is easily solvable with alias, but that's always felt a bit hackish and messy to me.
So, imagining we have a strongly typed 'enum' with no implicit conversion. Can we get back the convenience of being able to call numeric operators on them without casts or all the legwork involved?
Could the compiler not automatically generate them? The downside is that this would result in multiple copies of what was essentially the same function for int and every enum type.
We could make the programmer ask for it with a special base type, eg.
enum FLAG : numeric {}
but, ideally we want existing code to continue to work.
I wonder if the compiler could safely do the (cast) for us without loosing type safety? i.e. case the enum to int, call the int operator, and cast the result back.
The result would be that this works:
foo(FLAG.READ|FLAG.WRITE);
but this would error:
foo(FLAG.READ|FLAG.WRITE|5);
and enum would appear to be a strong type, and would no longer collide on function resolution but we'd have the convenience of numeric operators - a common usage pattern for enums. Are there other usage patterns this would break?
R

Regan Heath:
> I find being able to define bit flag values with an enum and combine them using | and |= or negate with &= ~flag etc very useful.
The cause of the problem here is that you are trying to use enums for a different purpose, as composable flags. In C# enums and flags are not the same thing, you can use the [Flags] attribute:
http://www.codeguru.com/vb/sample_chapter/article.php/c12963
The ridiculous thing of D development is that no one ever takes a look at C#, it often already contains a solution to problems we are just starting to find in D (or often that we just refuse to see in D). As they say: "Those who cannot learn from C# are doomed to re-invent it, often badly."
(In D you can solve this problem creating a flags struct, using a strategy similar to the one used by std.bitmanip.bitfields, but it feels hackish).
Bye,
bearophile