www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Implicit enum conversions are a stupid PITA

reply "Nick Sabalausky" <a a.a> writes:
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)).
Mar 23 2010
next sibling parent Marianne Gagnon <auria.mg gmail.com> writes:
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 ;)
Mar 23 2010
prev sibling next sibling parent reply yigal chripun <yigal100 gmail.com> writes:
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.
Mar 23 2010
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
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
Mar 23 2010
parent yigal chripun <yigal100 gmail.com> writes:
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.
Mar 23 2010
prev sibling next sibling parent yigal chripun <yigal100 gmail.com> writes:
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
Mar 23 2010
prev sibling parent reply "Nick Sabalausky" <a a.a> writes:
"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.
Mar 23 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
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
Mar 23 2010
prev sibling parent reply yigal chripun <yigal100 gmail.com> writes:
Nick Sabalausky Wrote:

 "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.

Regarding the base type notion, I re-phrased my inccurate saying above in a reply to my post. I don't agree that enums should have a base type, enums should be distinct storng types. The numeric value should be a *property* of an enum member and not define its identity. Which is how it works in Java 5 where each each member is a singelton class. you should never do: void foo(int); foo(MyEnum.Bar); // this is bad design instead do: foo(MyEnum.Bar.value); // value is a regular property. This is also more flexible, since you could do things like: // assume I defined a Color enum foo(Color.Red.ordinal); bar(Color.Red.rgb); where foo belongs to an API that defines a a list of colors (red is 5)and bar belongs to a different API that uses the rgb value (red is 0xff0000) how would you do that with a C style enum?
Mar 24 2010
parent "Nick Sabalausky" <a a.a> writes:
"yigal chripun" <yigal100 gmail.com> wrote in message 
news:hodruh$aif$1 digitalmars.com...
 Nick Sabalausky Wrote:

 "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.

Regarding the base type notion, I re-phrased my inccurate saying above in a reply to my post. I don't agree that enums should have a base type, enums should be distinct storng types. The numeric value should be a *property* of an enum member and not define its identity. Which is how it works in Java 5 where each each member is a singelton class. you should never do: void foo(int); foo(MyEnum.Bar); // this is bad design instead do: foo(MyEnum.Bar.value); // value is a regular property. This is also more flexible, since you could do things like: // assume I defined a Color enum foo(Color.Red.ordinal); bar(Color.Red.rgb); where foo belongs to an API that defines a a list of colors (red is 5)and bar belongs to a different API that uses the rgb value (red is 0xff0000) how would you do that with a C style enum?

I see what you mean. Personally I don't care if it's handled as a property or as an underlying type that can only be obtained via an explicit cast, as long as you never get the "associated" value implicitly.
Mar 24 2010
prev sibling next sibling parent reply Regan Heath <regan netmail.co.nz> writes:
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
Mar 24 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
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
Mar 24 2010
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/24/2010 08:28 AM, bearophile wrote:
 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).

There are many good things in many languages to look at. C# has its pluses and minuses, but is definitely not the be-all, end-all of PLs. In particular, economy of means does not seem to be one of C#'s strengths.
 As
 they say: "Those who cannot learn from C# are doomed to re-invent it,
 often badly."

Who are "they"?
 (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).

Why does a mechanism that allows creating bitfields, custom enums, flags, custom-base literals, and more, feel hackish, whereas dumping a wheelbarrow of narrow-usefulness features with every release while still failing to address major problems (concurrency, immutability) feels not? Andrei
Mar 24 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Andrei Alexandrescu:

See my other answers, I add just a small comment:

while still failing to address major problems (concurrency, immutability) feels
not?<

I am not yet able to say something meaningful about the concurrency design, so I don't comment about it. Flags are not so common, so I agree they are of secondary importance. But integral numbers, floating point values, enums, etc are not minor things. They are more important than concurrency and immutability because you use them all the time in programs, and they must be as well designed as possible, they are the foundation where all other things are written over. The less solid they are the more unstable your higher level things like concurrency will be. Bye, bearophile
Mar 24 2010
prev sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:hod5o3$1nhg$1 digitalmars.com...
 On 03/24/2010 08:28 AM, bearophile wrote:
 As
 they say: "Those who cannot learn from C# are doomed to re-invent it,
 often badly."

Who are "they"?

He was modifying the common expression "Those who don't learn from the past are doomed to repeat it."
 (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).

Why does a mechanism that allows creating bitfields, custom enums, flags, custom-base literals, and more, feel hackish,

Because it involves passing everything as parameters to a string-mixin-generating function/template. Powerful as such as thing is, and as much as I like having that ability available, it is a rather blunt instrument and does tend to feel very hackish. Also, looking at the docs for bitmanip, it looks like "bitfields" creates a "BitArray". But the interface for bitarray doesn't really seem to match the conceptual-level operations performed on bitfields any more than just using an ordinary uint would, and it doesn't seem to solve most of the problems with doing so, either.
 whereas dumping a wheelbarrow of narrow-usefulness features with every 
 release

Custom bitfields are extremely useful for low-level code. Being a self-proclaimed "systems" language, there's no reason D should consider such a thing to be of "narrow-usefulness".
 while still failing to address major problems (concurrency, immutability) 
 feels not?

I don't think anyone's suggesting that things like concurrency and immutability should fail to be addressed.
Mar 24 2010
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/24/2010 12:57 PM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org>  wrote in message
 news:hod5o3$1nhg$1 digitalmars.com...
 On 03/24/2010 08:28 AM, bearophile wrote:
 As
 they say: "Those who cannot learn from C# are doomed to re-invent it,
 often badly."

Who are "they"?

He was modifying the common expression "Those who don't learn from the past are doomed to repeat it."

But paraphrasing ain't "as they say" because they don't say that. Besides, I thought he's paraphrasing "Any sufficiently complicated C or Fortran program contains a buggy implementation of Common Lisp." But I guess that's just me being cranky - I'm sick.
 (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).

Why does a mechanism that allows creating bitfields, custom enums, flags, custom-base literals, and more, feel hackish,

Because it involves passing everything as parameters to a string-mixin-generating function/template. Powerful as such as thing is, and as much as I like having that ability available, it is a rather blunt instrument and does tend to feel very hackish.

Feeling is subjective. To me it doesn't.
 Also, looking at the docs for bitmanip, it looks like "bitfields" creates a
 "BitArray". But the interface for bitarray doesn't really seem to match the
 conceptual-level operations performed on bitfields any more than just using
 an ordinary uint would, and it doesn't seem to solve most of the problems
 with doing so, either.

Nonsense. bitfields does not create a BitArray and does exactly what you'd expect some bit fields to do. To saliently criticize an artifact, it does help to understand it.
 whereas dumping a wheelbarrow of narrow-usefulness features with every
 release

Custom bitfields are extremely useful for low-level code. Being a self-proclaimed "systems" language, there's no reason D should consider such a thing to be of "narrow-usefulness".

First, custom bitfields are taken care of appropriately by bitfield. You may want to try it before replying. Second, this thread is about enums that are bitwise flags, so I take it you replied to disagree with every paragraph I wrote.
 while still failing to address major problems (concurrency, immutability)
 feels not?

I don't think anyone's suggesting that things like concurrency and immutability should fail to be addressed.

Then stop extolling the virtues of an obscure feature. Andrei
Mar 24 2010
next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message 
news:hodla0$2sta$1 digitalmars.com...
 On 03/24/2010 12:57 PM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org>  wrote in message
 news:hod5o3$1nhg$1 digitalmars.com...
 Who are "they"?

past are doomed to repeat it."

But paraphrasing ain't "as they say" because they don't say that. Besides, I thought he's paraphrasing "Any sufficiently complicated C or Fortran program contains a buggy implementation of Common Lisp." But I guess that's just me being cranky - I'm sick.

Ok, I'm not going to get baited into picking apart minute details of someone's exact choice of wording.
 Why does a mechanism that allows creating bitfields, custom enums, 
 flags,
 custom-base literals, and more, feel hackish,

Because it involves passing everything as parameters to a string-mixin-generating function/template. Powerful as such as thing is, and as much as I like having that ability available, it is a rather blunt instrument and does tend to feel very hackish.

Feeling is subjective. To me it doesn't.

And so what, that proves it isn't hackish? Point is, there are people who do find it hackish, and saying "I don't" hardly addresses the issue.
 Also, looking at the docs for bitmanip, it looks like "bitfields" creates 
 a
 "BitArray". But the interface for bitarray doesn't really seem to match 
 the
 conceptual-level operations performed on bitfields any more than just 
 using
 an ordinary uint would, and it doesn't seem to solve most of the problems
 with doing so, either.

Nonsense. bitfields does not create a BitArray and does exactly what you'd expect some bit fields to do. To saliently criticize an artifact, it does help to understand it. First, custom bitfields are taken care of appropriately by bitfield. You may want to try it before replying. Second, this thread is about enums that are bitwise flags, so I take it you replied to disagree with every paragraph I wrote.

"it does help to understand it" <- Which is why I went and double-checked the docs. The docs didn't say anything about what "bitfields" actually created, but it did have a big definition of the "BitArray" type right there, and no other types were mentioned besides FloatRep and DoubleRep (which were clearly mere uses of "bitfields"), so I assumed. Clearly I assumed wrong, big fucking deal. That's no reason to get all pissy about it.
 while still failing to address major problems (concurrency, 
 immutability)
 feels not?

I don't think anyone's suggesting that things like concurrency and immutability should fail to be addressed.

Then stop extolling the virtues of an obscure feature.

Wow, cranky indeed.
Mar 24 2010
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/24/2010 02:34 PM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org>  wrote in message
 news:hodla0$2sta$1 digitalmars.com...
 On 03/24/2010 12:57 PM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org>   wrote in message
 news:hod5o3$1nhg$1 digitalmars.com...
 Who are "they"?

past are doomed to repeat it."

But paraphrasing ain't "as they say" because they don't say that. Besides, I thought he's paraphrasing "Any sufficiently complicated C or Fortran program contains a buggy implementation of Common Lisp." But I guess that's just me being cranky - I'm sick.

Ok, I'm not going to get baited into picking apart minute details of someone's exact choice of wording.
 Why does a mechanism that allows creating bitfields, custom enums,
 flags,
 custom-base literals, and more, feel hackish,

Because it involves passing everything as parameters to a string-mixin-generating function/template. Powerful as such as thing is, and as much as I like having that ability available, it is a rather blunt instrument and does tend to feel very hackish.

Feeling is subjective. To me it doesn't.

And so what, that proves it isn't hackish? Point is, there are people who do find it hackish, and saying "I don't" hardly addresses the issue.
 Also, looking at the docs for bitmanip, it looks like "bitfields" creates
 a
 "BitArray". But the interface for bitarray doesn't really seem to match
 the
 conceptual-level operations performed on bitfields any more than just
 using
 an ordinary uint would, and it doesn't seem to solve most of the problems
 with doing so, either.

Nonsense. bitfields does not create a BitArray and does exactly what you'd expect some bit fields to do. To saliently criticize an artifact, it does help to understand it. First, custom bitfields are taken care of appropriately by bitfield. You may want to try it before replying. Second, this thread is about enums that are bitwise flags, so I take it you replied to disagree with every paragraph I wrote.

"it does help to understand it"<- Which is why I went and double-checked the docs. The docs didn't say anything about what "bitfields" actually created, but it did have a big definition of the "BitArray" type right there, and no other types were mentioned besides FloatRep and DoubleRep (which were clearly mere uses of "bitfields"), so I assumed. Clearly I assumed wrong, big fucking deal. That's no reason to get all pissy about it.
 while still failing to address major problems (concurrency,
 immutability)
 feels not?

I don't think anyone's suggesting that things like concurrency and immutability should fail to be addressed.

Then stop extolling the virtues of an obscure feature.

Wow, cranky indeed.

Apology accepted :oD. Andrei
Mar 24 2010
prev sibling parent Bernard Helyer <b.helyer gmail.com> writes:
On 25/03/10 07:22, Andrei Alexandrescu wrote:
 On 03/24/2010 12:57 PM, Nick Sabalausky wrote:
 "Andrei Alexandrescu"<SeeWebsiteForEmail erdani.org> wrote in message
 news:hod5o3$1nhg$1 digitalmars.com...
 On 03/24/2010 08:28 AM, bearophile wrote:
 As
 they say: "Those who cannot learn from C# are doomed to re-invent it,
 often badly."

Who are "they"?

He was modifying the common expression "Those who don't learn from the past are doomed to repeat it."

But paraphrasing ain't "as they say" because they don't say that. Besides, I thought he's paraphrasing "Any sufficiently complicated C or Fortran program contains a buggy implementation of Common Lisp." But I guess that's just me being cranky - I'm sick.

"those who do not understand unix are doomed to reinvent it, poorly."
Mar 24 2010
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Nick Sabalausky wrote:
 Custom bitfields are extremely useful for low-level code. Being a 
 self-proclaimed "systems" language, there's no reason D should consider such 
 a thing to be of "narrow-usefulness".

I've written a lot of low-level code, I even programmed Mattel Intellivision cartridges and one of those old 70's hand-held LED games (Mattel Soccer). I've done a lot of embedded 6800 microprocessor work, device drivers, graphics drivers, etc. I've done a lot of bit twiddling code. But I find C bit fields to be generally worthless. One reason is the standard requires them to be right justified after extraction. This kills performance. I muck with with them in-place using biased arithmetic.
Mar 24 2010
parent reply "Nick Sabalausky" <a a.a> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:hoepts$2a98$1 digitalmars.com...
 Nick Sabalausky wrote:
 Custom bitfields are extremely useful for low-level code. Being a 
 self-proclaimed "systems" language, there's no reason D should consider 
 such a thing to be of "narrow-usefulness".

I've written a lot of low-level code, I even programmed Mattel Intellivision cartridges and one of those old 70's hand-held LED games (Mattel Soccer). I've done a lot of embedded 6800 microprocessor work, device drivers, graphics drivers, etc. I've done a lot of bit twiddling code. But I find C bit fields to be generally worthless. One reason is the standard requires them to be right justified after extraction. This kills performance. I muck with with them in-place using biased arithmetic.

Actually, with "bitfields", I've been mostly referring to pretty much just that: doing manual bit-twiddling, typically aided by manifest constants and/or enums, and taking the stance that doing that could use a better (ie, more abstracted and more type-safe) interface (while still keeping the same under-the-hood behavior). Maybe it's all the low-level stuff I've done, but any time I come across the term "bitfield" I instinctively envision those abstract rows of labeled "bit" squares (or differently-sized rectangles) that you see in spec sheets for digital hardware (ie, the abstract concept of a small piece of memory having bit-aligned data), rather than specifically the structs-with-sub-byte-member-alignment that I keep forgetting C has. I can't really comment on that latter kind as I've never really used them (can't remember why not), although I can easily believe that they may be insufficient for the job. Maybe that difference is where the disagreement between me and Andrei arose.
Mar 25 2010
parent Walter Bright <newshound1 digitalmars.com> writes:
Nick Sabalausky wrote:
 Actually, with "bitfields", I've been mostly referring to pretty much just 
 that: doing manual bit-twiddling, typically aided by manifest constants 
 and/or enums, and taking the stance that doing that could use a better (ie, 
 more abstracted and more type-safe) interface (while still keeping the same 
 under-the-hood behavior).
 
 Maybe it's all the low-level stuff I've done, but any time I come across the 
 term "bitfield" I instinctively envision those abstract rows of labeled 
 "bit" squares (or differently-sized rectangles) that you see in spec sheets 
 for digital hardware (ie, the abstract concept of a small piece of memory 
 having bit-aligned data), rather than specifically the 
 structs-with-sub-byte-member-alignment that I keep forgetting C has. I can't 
 really comment on that latter kind as I've never really used them (can't 
 remember why not), although I can easily believe that they may be 
 insufficient for the job. Maybe that difference is where the disagreement 
 between me and Andrei arose.

It does seem we've totally misunderstood each other. Yes, I was referring to (and I'm sure Andrei was as well) the C bitfield language feature.
Mar 25 2010
prev sibling parent reply Regan Heath <regan netmail.co.nz> writes:
bearophile wrote:
 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."

Thanks, I haven't used the flags attribute in C# before. It is like the "numeric base type" suggestion I made, in that it causes an alternate set of behaviour for 'enum'. I was hoping there was some way to get what we want without extra syntax.
 (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).

I've not used that (it's been a while since I used D for anything practical) but it seems like it's not quite what we want, we really want a set of compile time constants. R
Mar 24 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Regan Heath:
 but it seems like it's not quite what we want, we really want 
 a set of compile time constants.

I meant using the same design strategy. See below. -------------------- Andrei:
In particular, economy of means does not seem to be one of C#'s strengths.<

I agree, surely C# is not a compact and orthogonal language. C# devs are aware of this, you can see it from the very low number of features they have added to C#4.
Who are "they"?<

Almost everyone that posts on this newsgroup :-) I put myself in the group too, because I am not expert of C# and many times in the past I have "invented" things for D that later I have found in C#, sometimes with a design better than mine.
Why does a mechanism that allows creating bitfields, custom enums, flags,
custom-base literals, and more, feel hackish, whereas dumping a wheelbarrow of
narrow-usefulness features with every release while still failing to address
major problems (concurrency, immutability) feels not?<

I agree with you that too many features turn a language in a ball of mud. On the other hand an excess of abstraction or relying too much on the composition of elementary parts makes writing code like solving a Rubik cube (see Scheme language or your "find" algorithm in Phobos2). In this case it may be acceptable to use compile-time strings to create a flags-like struct, I'd like to write it :-) But I am not sure it's the best solution. I have to judge when it's done. Maybe in this case the C# solution is the best. The main of my precedent post was that you can't allow arithmetic operations on enums just because you may want to use them as bit flags. They are two different purposes. In this case C# tells the two usages apart in a clear way, instead of mudding them together. If the purpose is clear and well defined, and the usage is safe, I think programmers don't have too much hard time remembering the purpose of fields. That's why I don't think introducing "many" keywords is bad, it's better to use something like "autoconst" than using "inout" to save a keyword, that's unrelated to the its purpose. The lesson given by the success of large languages like C# is that programmers are able to remember and use many things if their purpose is clear and defined in a semantically tidy way. (I think the current design decisions of D enums are not the best ones.) The code of std.bitmanip.bitfields is hard or impossible to understand, to read, to fix, to modify, to improve. Compile-time strings used as macros are acceptable for one-liners, but your (nicely safe!) bitfields shows that it's not fit when you want to push it to that complexity level. Bye, bearophile
Mar 24 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
To invent a software you can first find the best syntax. This seems a nice
syntax, very similar to the enum one (that ubyte is optional):

flags ubyte Todo {
    do_nothing,
    walk_dog,
    cook_breakfast,
    deliver_newspaper,
    visit_miss_kerbopple,
    wash_covers
}

Todo todo = Todo.walk_dog | Todo.deliver_newspaper | Todo.wash_covers;
if (todo == (Todo.walk_dog | Todo.deliver_newspaper)) { ...
if ((Todo.walk_dog | Todo.deliver_newspaper) in todo) { ...
if ((Todo.walk_dog | Todo.deliver_newspaper) & todo) { ...
assert((Todo.walk_dog | Todo.walk_dog) == Todo.walk_dog); // OK


A way to implement it with current D2 syntax:


alias Flags!(ubyte, "do_nothing",
                    "walk_dog"
                    "cook_breakfast"
                    "deliver_newspaper"
                    "visit_miss_kerbopple"
                    "wash_covers") Todo;


Where Flags defines a struct, "do_nothing" are compile-time constants. It can
overload 8 operators:
=   ==   |    |=    in   &   &=  opBool

The operator ! too can be defined, but I think it looks too much like the | so
it can be omitted (other operators like ^ and ~ are possible).


Something like this can't work if enums become tidier:

enum ubyte Todo {
    mixin(Flags);
    do_nothing,
    walk_dog,
    cook_breakfast,
    deliver_newspaper,
    visit_miss_kerbopple,
    wash_covers
}



I don't like this:

mixin(Flags("
enum ubyte Todo {
    do_nothing,
    walk_dog,
    cook_breakfast,
    deliver_newspaper,
    visit_miss_kerbopple,
    wash_covers
}
"));




Something like can worl, but it's not nice:

struct Todo {
    mixin(Fields!(ubyte, "do_nothing",
                         "walk_dog"
                         "cook_breakfast"
                         "deliver_newspaper"
                         "visit_miss_kerbopple"
                         "wash_covers");
}



Once the programmer can define attributes, it can be doable this syntax that
adds the required methods to the enum, but I am not sure:

 fields enum ubyte Todo {
    do_nothing,
    walk_dog,
    cook_breakfast,
    deliver_newspaper,
    visit_miss_kerbopple,
    wash_covers
}

Bye,
bearophile
Mar 24 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Another possible syntax that I don't like:

private enum ubyte enum_Todo {
    do_nothing,
    walk_dog,
    cook_breakfast,
    deliver_newspaper,
    visit_miss_kerbopple,
    wash_covers
}

alias Flags!(enum_Todo) Todo;

(Essentially the  flags attribute can do this, and avoid to define the enum).


This doesn't work, you can't pass locally defined anonymous enums to templates:

alias Flags!(enum ubyte { do_nothing,
                          walk_dog,
                          cook_breakfast,
                          deliver_newspaper,
                          visit_miss_kerbopple,
                          wash_covers
                        }) Todo;

Bye,
bearophile
Mar 24 2010
prev sibling parent reply Lutger <lutger.blijdestijn gmail.com> writes:
bearophile wrote:

 To invent a software you can first find the best syntax. This seems a nice
 syntax, very similar to the enum one (that ubyte is optional):
 
 flags ubyte Todo {
     do_nothing,
     walk_dog,
     cook_breakfast,
     deliver_newspaper,
     visit_miss_kerbopple,
     wash_covers
 }
 
 Todo todo = Todo.walk_dog | Todo.deliver_newspaper | Todo.wash_covers;
 if (todo == (Todo.walk_dog | Todo.deliver_newspaper)) { ...
 if ((Todo.walk_dog | Todo.deliver_newspaper) in todo) { ...
 if ((Todo.walk_dog | Todo.deliver_newspaper) & todo) { ...
 assert((Todo.walk_dog | Todo.walk_dog) == Todo.walk_dog); // OK
 
 
 A way to implement it with current D2 syntax:
 
 
 alias Flags!(ubyte, "do_nothing",
                     "walk_dog"
                     "cook_breakfast"
                     "deliver_newspaper"
                     "visit_miss_kerbopple"
                     "wash_covers") Todo;
 
 
 Where Flags defines a struct, "do_nothing" are compile-time constants. It
 can overload 8 operators:
 =   ==   |    |=    in   &   &=  opBool
 
 The operator ! too can be defined, but I think it looks too much like the |
 so it can be omitted (other operators like ^ and ~ are possible).
 

I like this idea of implementing a flag type and tried to work something out. Instead of implementing the overloads, it is also possible to generate an enum via CTFE inside a struct and forward with alias this, what do you think? I have tried this syntax, seems to work ok: alias Flags!q{ do_nothing, walk_dog, cook_breakfast, deliver_newspaper, visit_miss_kerbopple, wash_covers } Todo; It does allow this though, but perhaps that can fixed: Todo todo = Todo.walk_dog; todo |= 4; With such a type, it is easy to add some small convenience features, such as an enumToString, define property .max and implement a range that iterates over all flags set or possible values.
Mar 28 2010
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Lutger:
 alias Flags!q{ do_nothing, 
                walk_dog, 
                cook_breakfast,
                deliver_newspaper,
                visit_miss_kerbopple,
                wash_covers } Todo;

That's better. But you have missed the optional type :-) Bye, bearophile
Mar 28 2010
parent reply Lutger <lutger.blijdestijn gmail.com> writes:
bearophile wrote:

 Lutger:
 alias Flags!q{ do_nothing,
                walk_dog,
                cook_breakfast,
                deliver_newspaper,
                visit_miss_kerbopple,
                wash_covers } Todo;

That's better. But you have missed the optional type :-) Bye, bearophile

Thanks. I omitted the type because I was lazy. :) Assuming uint it is possible to just use std.intrinsic for bit twiddling in the range, since I'm not so good with that. I can brush it up and post the code later if you want, but it's not so pretty.
Mar 28 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Lutger:
I can brush it up and post the code later if you want, but it's not so pretty.<

This syntax is not pretty, but I think it can be acceptable: alias Flags!q{ do_nothing, walk_dog, cook_breakfast, deliver_newspaper, visit_miss_kerbopple, wash_covers } Todo; With optional type too: alias Flags!q{ int : do_nothing, walk_dog, cook_breakfast, deliver_newspaper, visit_miss_kerbopple, wash_covers } Todo; Now it's a matter of writing a good amount of CTFE to implement the little parser, to create the methods and attributes, to perform the necessary sanity tests in all inputs, to write documentation, tests, etc :o) I don't need flags often in my code so don't implement it for me. If you have some free time you can write it for other people here. Once the code is good probably Andrei will accept to put it in Phobos2. Bye, bearophile
Mar 28 2010
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/28/2010 03:44 AM, Lutger wrote:
 bearophile wrote:

 To invent a software you can first find the best syntax. This seems a nice
 syntax, very similar to the enum one (that ubyte is optional):

 flags ubyte Todo {
      do_nothing,
      walk_dog,
      cook_breakfast,
      deliver_newspaper,
      visit_miss_kerbopple,
      wash_covers
 }

 Todo todo = Todo.walk_dog | Todo.deliver_newspaper | Todo.wash_covers;
 if (todo == (Todo.walk_dog | Todo.deliver_newspaper)) { ...
 if ((Todo.walk_dog | Todo.deliver_newspaper) in todo) { ...
 if ((Todo.walk_dog | Todo.deliver_newspaper)&  todo) { ...
 assert((Todo.walk_dog | Todo.walk_dog) == Todo.walk_dog); // OK


 A way to implement it with current D2 syntax:


 alias Flags!(ubyte, "do_nothing",
                      "walk_dog"
                      "cook_breakfast"
                      "deliver_newspaper"
                      "visit_miss_kerbopple"
                      "wash_covers") Todo;


 Where Flags defines a struct, "do_nothing" are compile-time constants. It
 can overload 8 operators:
 =   ==   |    |=    in&    &=  opBool

 The operator ! too can be defined, but I think it looks too much like the |
 so it can be omitted (other operators like ^ and ~ are possible).

I like this idea of implementing a flag type and tried to work something out. Instead of implementing the overloads, it is also possible to generate an enum via CTFE inside a struct and forward with alias this, what do you think? I have tried this syntax, seems to work ok: alias Flags!q{ do_nothing, walk_dog, cook_breakfast, deliver_newspaper, visit_miss_kerbopple, wash_covers } Todo; It does allow this though, but perhaps that can fixed: Todo todo = Todo.walk_dog; todo |= 4; With such a type, it is easy to add some small convenience features, such as an enumToString, define property .max and implement a range that iterates over all flags set or possible values.

I think it will take some time to settle the competition between "template parses everything" approach and "language parses most" approach. For what it's worth, Walter and I were talking three years ago about a "new alias" feature: template Flags(new alias Names...) { ... } "new alias" means that you may pass to Flags symbols that haven't been defined. With that feature in place, Flags could be used like this: alias Flags!(do_nothing, walk_dog, cook_breakfast, deliver_newspaper, visit_miss_kerbopple, wash_covers) Todo; No muss, no fuss, no need to stringize anything. Flags could get to the names of the alias parameters by using Names[i].stringof. Well that's not in the language yet :o). Andrei
Mar 28 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Andrei Alexandrescu:
 For what it's worth, Walter and I were talking three years ago about a 
 "new alias" feature:
 
 template Flags(new alias Names...) { ... }
 
 "new alias" means that you may pass to Flags symbols that haven't been 
 defined. With that feature in place, Flags could be used like this:
 
   alias Flags!(do_nothing,
                walk_dog,
                cook_breakfast,
                deliver_newspaper,
                visit_miss_kerbopple,
                wash_covers)
       Todo;
 
 No muss, no fuss, no need to stringize anything.

Maybe it's better to go all the way and just add symbols to the language, as in Ruby, Lisps, etc. I don't know. The #walk_dog syntax is not available to represent a symbol "walk_dog", I presume... Bye, bearophile
Mar 28 2010
prev sibling parent reply Lutger <lutger.blijdestijn gmail.com> writes:
Andrei Alexandrescu wrote:

 On 03/28/2010 03:44 AM, Lutger wrote:

 I like this idea of implementing a flag type and tried to work something
 out. Instead of implementing the overloads, it is also possible to
 generate an enum via CTFE inside a struct and forward with alias this,
 what do you think? I have tried this syntax, seems to work ok:

 alias Flags!q{ do_nothing,
                 walk_dog,
                 cook_breakfast,
                 deliver_newspaper,
                 visit_miss_kerbopple,
                 wash_covers } Todo;

 It does allow this though, but perhaps that can fixed:

 Todo todo = Todo.walk_dog;
 todo |= 4;

 With such a type, it is easy to add some small convenience features, such
 as
 an  enumToString, define property .max and implement a range that iterates
 over all flags set or possible values.

I think it will take some time to settle the competition between "template parses everything" approach and "language parses most" approach. For what it's worth, Walter and I were talking three years ago about a "new alias" feature: template Flags(new alias Names...) { ... } "new alias" means that you may pass to Flags symbols that haven't been defined. With that feature in place, Flags could be used like this: alias Flags!(do_nothing, walk_dog, cook_breakfast, deliver_newspaper, visit_miss_kerbopple, wash_covers) Todo; No muss, no fuss, no need to stringize anything. Flags could get to the names of the alias parameters by using Names[i].stringof. Well that's not in the language yet :o). Andrei

That would be nice, perhaps it's possible for a backwards-compatible 2.x release to work something out. As bearophile said Ruby has some nice things for metaprogramming that help with this, borrowed from lisp of course. I do think Ruby's strong metaprogramming helped RoR become popular. In the meantime I tried to hack Flags together and got stuck on this thing: auto myTodoList = Todo.do_nothing; Now myTodoList is of type uint (or whatever), not so nice - it's inconsistent with regular named enums. It messed up making a range for iterating over the flags set, which I thought was a nice addition. Anyway, here is my attempt so far, please forgive me it is not so elegant: import std.conv; string genFlags(string names, string type = "uint") { string result = "enum : " ~ type ~ " { "; int bits = 1; size_t pos; // skip leading whitespace while( ++pos < names.length && names[pos] == ' ') { } size_t prev = pos; bool hasInitializer = false; while ( pos < names.length) { if (names[pos] == '=') hasInitializer = true; else if (names[pos] == ',') { result ~= names[prev..pos]; if (!hasInitializer) { result ~= " = " ~ std.conv.to!string(bits); bits <<= 1; } else hasInitializer = false; result ~= ','; //skip whitespace while( ++pos < names.length && names[pos] == ' ') { } prev = pos; } pos++; } // concat the last flag if (hasInitializer) return result ~ names[prev..pos] ~ "}"; return result ~ names[prev..pos] ~ " = " ~ std.conv.to!string(bits) ~ "}"; } struct Flags(string members, T = uint) { static assert( is(T : ulong), "Wrong underlying type of Flags: must be integral not " ~ T.stringof ); mixin( genFlags(members) ); alias typeof(this) FlagsType; T value; alias value this; this(T value) { this.value = value; } void opAssign(T value) { this.value = value; } pure bool opBinaryRight(string op, E)(E lhs) const if ( op == "in" ) { return cast(bool)(lhs & this); } } unittest { alias Flags!q{ do_nothing, walk_dog, cook_breakfast, deliver_newspaper, visit_miss_kerbopple, morning_task = walk_dog | cook_breakfast, wash_covers } Todo; Todo list1 = Todo.do_nothing; assert( list1 == 1 ); list1 |= Todo.wash_covers | Todo.walk_dog; assert(list1 == Todo.do_nothing | Todo.wash_covers | Todo.walk_dog); assert(Todo.do_nothing in list1); Todo list2 = Todo.cook_breakfast | Todo.wash_covers; assert( list1 & list2 == Todo.do_nothing | Todo.cook_breakfast); list1 = list2; assert(list1 == Todo.do_nothing | Todo.wash_covers | Todo.walk_dog); assert( Todo.morning_task == Todo.walk_dog | Todo.cook_breakfast ); auto list3 = Todo.deliver_newspaper; assert(Todo.deliver_newspaper in list3, "can't infer type properly"); /* bug */ }
Mar 29 2010
next sibling parent Lutger <lutger.blijdestijn gmail.com> writes:
Lutger wrote:

...

 struct Flags(string members, T = uint)
 {
     static assert( is(T : ulong), "Wrong underlying type of Flags: must be
 integral not " ~ T.stringof );
 
     mixin( genFlags(members) );

I screwed up of course, this must be: mixin( genFlags(members, T.stringof) );
     alias typeof(this) FlagsType;
 
     T value;
     alias value this;
 
     this(T value)
     {
         this.value = value;
     }
 
     void opAssign(T value)
     {
         this.value = value;
     }
 
     pure bool opBinaryRight(string op, E)(E lhs)  const
         if ( op == "in" )
     {
         return cast(bool)(lhs & this);
     }
 }

Mar 29 2010
prev sibling parent Lutger <lutger.blijdestijn gmail.com> writes:
Lutger wrote:

...
 
 unittest
 {
     alias Flags!q{ do_nothing, walk_dog, cook_breakfast, deliver_newspaper,
         visit_miss_kerbopple, morning_task = walk_dog | cook_breakfast,
         wash_covers } Todo;
 
     Todo list1 = Todo.do_nothing;
     assert( list1 == 1 );
     list1 |= Todo.wash_covers | Todo.walk_dog;
     assert(list1 == Todo.do_nothing | Todo.wash_covers | Todo.walk_dog);
     assert(Todo.do_nothing in list1);
 
     Todo list2 = Todo.cook_breakfast | Todo.wash_covers;
     assert( list1 & list2 == Todo.do_nothing | Todo.cook_breakfast);
     list1 = list2;
     assert(list1 == Todo.do_nothing | Todo.wash_covers | Todo.walk_dog);
 
     assert( Todo.morning_task == Todo.walk_dog | Todo.cook_breakfast );
 
     auto list3 = Todo.deliver_newspaper;
     assert(Todo.deliver_newspaper in list3, "can't infer type properly");
     /*
 bug */
 }

^ oops, this one is totally messed up and needs to go to precedence school. This should be better: unittest { alias Flags!q{ do_nothing, walk_dog, cook_breakfast, deliver_newspaper, visit_miss_kerbopple, morning_task = walk_dog | cook_breakfast, wash_covers } Todo; Todo list1 = Todo.do_nothing; assert( list1 == 1 ); list1 |= Todo.wash_covers | Todo.walk_dog; assert(list1 == (Todo.do_nothing | Todo.wash_covers | Todo.walk_dog) ); assert(Todo.do_nothing in list1); Todo list2 = Todo.cook_breakfast | Todo.wash_covers; assert( (list1 & list2) == Todo.wash_covers ); list1 = list2; assert(list1 == (Todo.cook_breakfast | Todo.wash_covers) ); assert( Todo.morning_task == (Todo.walk_dog | Todo.cook_breakfast) ); auto list3 = Todo.deliver_newspaper; /* bug */ assert(Todo.deliver_newspaper in list3, "can't infer type properly"); }
Mar 29 2010
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Nick Sabalausky wrote:
 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?)


------- a.d ------------------- enum FooA { fooA }; void bar(FooA x) {} ------- test.d ---------------- import a; enum FooB { fooB }; void bar(FooB x) {} void test() { bar(FooB.fooB); } ------------------------------ with: dmd test I do not get an error with either D1 or D2. So, if you are getting an error, how is your code different?
Mar 24 2010
next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:hoepeu$28po$1 digitalmars.com...
 Nick Sabalausky wrote:
 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?)


------- a.d ------------------- enum FooA { fooA }; void bar(FooA x) {} ------- test.d ---------------- import a; enum FooB { fooB }; void bar(FooB x) {} void test() { bar(FooB.fooB); } ------------------------------ with: dmd test I do not get an error with either D1 or D2. So, if you are getting an error, how is your code different?

Hmm...That's odd...I guess I'll have to dig closer to see what's going on. The discussion on "D.learn" didn't seem to indicate that that above should work, so I hastily ruled out the possibility of something else going on in my code. It's too late for me to think stright ATM, so I'll have to check on that in the morning.
Mar 25 2010
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Nick Sabalausky wrote:
 Hmm...That's odd...I guess I'll have to dig closer to see what's going on. 
 The discussion on "D.learn" didn't seem to indicate that that above should 
 work, so I hastily ruled out the possibility of something else going on in 
 my code. It's too late for me to think stright ATM, so I'll have to check on 
 that in the morning.

This kind of misunderstanding comes up all the time, it's why having exact reproducible snippets of code is so important! Often the critical detail is something that was omitted because the person thought it was either obvious or irrelevant.
Mar 25 2010
parent reply "Nick Sabalausky" <a a.a> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:hofb6v$h5g$1 digitalmars.com...
 This kind of misunderstanding comes up all the time, it's why having exact 
 reproducible snippets of code is so important! Often the critical detail 
 is something that was omitted because the person thought it was either 
 obvious or irrelevant.

Yea, normally I'm in the habit of doing that for that very reason. Figures it would bite me the one time I don't. Anyway, this is what I'm doing, and it's giving me a conflict error on the call to 'bar' in 'main' with DMD 1.056 (fortunately, however, it seems to work fine in 2.042, so I guess the situation's improved in D2): // -------- separateLibrary.d -------- module separateLibrary; string makeFooBar(string name) { return " enum "~name~" { foo } void bar("~name~" e) {} "; } mixin(makeFooBar("FooA")); // -------- appModule.d -------- module appModule; import separateLibrary; mixin(makeFooBar("FooB")); // -------- main.d -------- module main; import separateLibrary; import appModule; void main() { bar(FooB.foo); // Error! 'bar' conflict } Compiled with "rdmd main"
Mar 25 2010
parent Walter Bright <newshound1 digitalmars.com> writes:
Nick Sabalausky wrote:
 Anyway, this is what I'm doing, and it's giving me a conflict error on the 
 call to 'bar' in 'main' with DMD 1.056 (fortunately, however, it seems to 
 work fine in 2.042, so I guess the situation's improved in D2):

The mixins obscure what is going on. The same error is reproduced with the simpler: --- a.d --- enum FooA { foo } void bar(FooA e) { } --- b.d --- enum FooB { foo } void bar(FooB e) { } --- test.d --- import a; import b; void main() { bar(FooB.foo); // Error! 'bar' conflict } -------------- D1 uses the earlier anti-hijacking system which disallows overloading of functions from different imports, unless they are specifically combined using an alias declaration. D2 improves this using Andrei's suggestion that such overloading be allowed if and only if functions from exactly one of those imports are potential matches. This is why the example code works on D2. If we change the declaration of bar in a.d to: --- a.d --- void bar(int e) { } ----------- then the compilation under D2 fails, because both a.bar and b.bar are now candidates, due to implicit conversion rules.
Mar 26 2010
prev sibling parent reply Ellery Newcomer <ellery-newcomer utulsa.edu> writes:
On 03/24/2010 11:40 PM, Walter Bright wrote:
 Nick Sabalausky wrote:
 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?)


------- a.d ------------------- enum FooA { fooA }; void bar(FooA x) {} ------- test.d ---------------- import a; enum FooB { fooB }; void bar(FooB x) {} void test() { bar(FooB.fooB); } ------------------------------ with: dmd test I do not get an error with either D1 or D2. So, if you are getting an error, how is your code different?

------- a.d ------------------- enum FooA { fooA }; void bar(FooA x) {} ------- test.d ---------------- import a; mixin(` enum FooB { fooB }; void bar(FooB x) {} `); void test() { bar(FooA.fooA); //error bar(FooB.fooB); } ------------------------------ ------- a.d ------------------- enum FooA { fooA }; void bar(FooA x) {} ------- test.d ---------------- import a; alias a.bar bar; mixin(` enum FooB { fooB }; void bar(FooB x) {} `); void test() { bar(FooA.fooA); bar(FooB.fooB); //error } ------------------------------ Somewhat OT: I wish enums had something analogous to struct's tupleof, or some way to enumerate the values of the enum, and maybe their names too. Do they?
Mar 25 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Ellery Newcomer:
 Somewhat OT: I wish enums had something analogous to struct's tupleof, 
 or some way to enumerate the values of the enum, and maybe their names 
 too. Do they?

D2 now gives you the tools to do that, even if it's bad looking code: import std.stdio: writeln; enum Foo { Aa, Bb, Cc } void main() { foreach (name; __traits(allMembers, Foo)) writeln(name, " ", mixin("cast(int)Foo." ~ name)); } Output: Aa 0 Bb 1 Cc 2 Bye, bearophile
Mar 25 2010
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Ellery Newcomer wrote:
 On 03/24/2010 11:40 PM, Walter Bright wrote:
 Nick Sabalausky wrote:
 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?)


------- a.d ------------------- enum FooA { fooA }; void bar(FooA x) {} ------- test.d ---------------- import a; enum FooB { fooB }; void bar(FooB x) {} void test() { bar(FooB.fooB); } ------------------------------ with: dmd test I do not get an error with either D1 or D2. So, if you are getting an error, how is your code different?

------- a.d ------------------- enum FooA { fooA }; void bar(FooA x) {} ------- test.d ---------------- import a; mixin(` enum FooB { fooB }; void bar(FooB x) {} `); void test() { bar(FooA.fooA); //error bar(FooB.fooB); } ------------------------------

This error is quite correct (and happens with or without the mixin). The idea is if you define a function in a local scope, it *completely hides* functions with the same name in imported scopes. In order to overload local functions with imported ones, an alias is necessary to bring the imported functions into the same scope. This allows the user complete control over which functions get overloaded when they come from diverse, and possibly completely independent, imports.
 ------- a.d -------------------
 enum FooA { fooA };
 void bar(FooA x) {}
 ------- test.d ----------------
 import a;
 alias a.bar bar;
 mixin(`
 enum FooB { fooB };
 void bar(FooB x) {}
 `);
 
 void test()
 {
     bar(FooA.fooA);
     bar(FooB.fooB); //error
 }
 ------------------------------

I'm not sure why this error is happening, it definitely has something to do with the mixin. Let me look into it some more.
Mar 25 2010
next sibling parent reply Ellery Newcomer <ellery-newcomer utulsa.edu> writes:
On 03/25/2010 02:00 PM, Walter Bright wrote:
 ------- a.d -------------------
 enum FooA { fooA };
 void bar(FooA x) {}
 ------- test.d ----------------
 import a;
 mixin(`
 enum FooB { fooB };
 void bar(FooB x) {}
 `);

 void test()
 {
 bar(FooA.fooA); //error
 bar(FooB.fooB);
 }
 ------------------------------

This error is quite correct (and happens with or without the mixin). The idea is if you define a function in a local scope, it *completely hides* functions with the same name in imported scopes. In order to overload local functions with imported ones, an alias is necessary to bring the imported functions into the same scope.

I know. It was my understanding that this entire thread was a complaint of this behavior.
 This allows the user complete control over which functions get
 overloaded when they come from diverse, and possibly completely
 independent, imports.

It's a very explicit form of control. Suppose I have 10 000 enums (I don't have quite that many, but there are a lot), each of which is in a different module because each is logically connected to something else. Because enums in D are too spartan for my tastes, I define a mixin to generate an enum, along with several helper functions, like name, fromInt, toInt, etc. (Note that a way to enumerate an enum's values would obviate all this, and __traits isn't an option in D1) It's my understanding that enums are strongly enough typed that calling such a helper function with an enum (or anything else) not defined for it would generate an error. So there shouldn't be any way to hijack it. But if I have some enum A defined in my current module and I also want to use enums modl2.B, modl3.C, etc, then I have to manually add all those alias modl2.name name; <poke> or else write an IDE tool that automatically generates them for me </poke> I guess what I'm trying to say is it doesn't make sense that I can implicitly import FooA, an external symbol, but not bar(FooA), an external symbol defined on an external symbol which cannot be implicitly converted to a local symbol.
 ------- a.d -------------------
 enum FooA { fooA };
 void bar(FooA x) {}
 ------- test.d ----------------
 import a;
 alias a.bar bar;
 mixin(`
 enum FooB { fooB };
 void bar(FooB x) {}
 `);

 void test()
 {
 bar(FooA.fooA);
 bar(FooB.fooB); //error
 }
 ------------------------------

I'm not sure why this error is happening, it definitely has something to do with the mixin. Let me look into it some more.

Mar 25 2010
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Ellery Newcomer wrote:
 I guess what I'm trying to say is it doesn't make sense that I can 
 implicitly import FooA, an external symbol, but not bar(FooA), an 
 external symbol defined on an external symbol which cannot be implicitly 
 converted to a local symbol.

And I believe it makes perfect sense! Everywhere else in the language, when you define a local name it *overrides* names in other scopes, it doesn't overload them.
Mar 25 2010
parent reply "Nick Sabalausky" <a a.a> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:hogmgm$oco$1 digitalmars.com...
 Ellery Newcomer wrote:
 I guess what I'm trying to say is it doesn't make sense that I can 
 implicitly import FooA, an external symbol, but not bar(FooA), an 
 external symbol defined on an external symbol which cannot be implicitly 
 converted to a local symbol.

And I believe it makes perfect sense! Everywhere else in the language, when you define a local name it *overrides* names in other scopes, it doesn't overload them.

Well, the result of that is that I'm forced to make my "genEnum" library utility generate "enum{name of enum}ToString({name of enum} e)" instead of "enumToString({name of enum} e)" or else users won't be able to use it without a bunch of odd alias contortions that I'm not sure I can wave away by including them in the original mixin. (I would have just called it "toString", but at the time, that had been giving me some strange troubles so I changed it to "enumToString" instead. In retrospect, it was probably giving me trouble because of this very same issue.) Of course, I can live with this as long as it's fixed in D2 (as seemed to be the case from the three-module test code I put in another post). But now that you're saying this I'm confused as to why that example I posted suddenly worked when I compiled it with D2.
Mar 25 2010
next sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Nick Sabalausky wrote:
 But now 
 that you're saying this I'm confused as to why that example I posted 
 suddenly worked when I compiled it with D2.

I haven't looked at that yet, just discussed the principle.
Mar 25 2010
prev sibling parent Ellery Newcomer <ellery-newcomer utulsa.edu> writes:
On 03/25/2010 05:26 PM, Nick Sabalausky wrote:
 "Walter Bright"<newshound1 digitalmars.com>  wrote in message
 news:hogmgm$oco$1 digitalmars.com...
 Ellery Newcomer wrote:
 I guess what I'm trying to say is it doesn't make sense that I can
 implicitly import FooA, an external symbol, but not bar(FooA), an
 external symbol defined on an external symbol which cannot be implicitly
 converted to a local symbol.

And I believe it makes perfect sense! Everywhere else in the language, when you define a local name it *overrides* names in other scopes, it doesn't overload them.

Well, the result of that is that I'm forced to make my "genEnum" library utility generate "enum{name of enum}ToString({name of enum} e)" instead of "enumToString({name of enum} e)" or else users won't be able to use it without a bunch of odd alias contortions that I'm not sure I can wave away by including them in the original mixin. (I would have just called it "toString", but at the time, that had been giving me some strange troubles so I changed it to "enumToString" instead. In retrospect, it was probably giving me trouble because of this very same issue.)

Suggestion: forget about enums. Implement something based on structs or classes a la Java, which gives you e.toString instead of toString(e) From this conversation, I'm getting the idea that enums are more an antifeature than anything. And you're using mixins already anyways.
Mar 25 2010
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Walter Bright wrote:
 I'm not sure why this error is happening, it definitely has something to 
 do with the mixin. Let me look into it some more.

Bug report and fix: http://d.puremagic.com/issues/show_bug.cgi?id=4011
Mar 26 2010
parent Ellery Newcomer <ellery-newcomer utulsa.edu> writes:
On 03/26/2010 02:01 PM, Walter Bright wrote:
 Walter Bright wrote:
 I'm not sure why this error is happening, it definitely has something
 to do with the mixin. Let me look into it some more.

Bug report and fix: http://d.puremagic.com/issues/show_bug.cgi?id=4011

Awesome!
Mar 26 2010
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Nick Sabalausky wrote:
 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".

You're certainly not the first to feel this way about implicit conversions. Niklaus Wirth did the same, and designed Pascal with no implicit conversions. You had to do an explicit cast each time. Man, what a royal pain in the ass that makes coding in Pascal. Straightforward coding, like converting a string of digits to an integer, becomes a mess of casts. Even worse, casts are a blunt instrument that *destroys* type checking (that wasn't so much of a problem with Pascal with its stone age abstract types, but it would be killer for D). Implicit integral conversions are not without problems, but when I found C I threw Pascal under the nearest bus and never wrote a line in it again. The taste was so bad, I refused to even look at Modula II and its failed successors. D has 12 integral types. Disabling implicit integral conversions would make it unbearable to use.
Mar 24 2010
next sibling parent "Nick Sabalausky" <a a.a> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:hoeukp$2kgv$1 digitalmars.com...
 Nick Sabalausky wrote:
 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".

You're certainly not the first to feel this way about implicit conversions. Niklaus Wirth did the same, and designed Pascal with no implicit conversions. You had to do an explicit cast each time. Man, what a royal pain in the ass that makes coding in Pascal. Straightforward coding, like converting a string of digits to an integer, becomes a mess of casts. Even worse, casts are a blunt instrument that *destroys* type checking (that wasn't so much of a problem with Pascal with its stone age abstract types, but it would be killer for D). Implicit integral conversions are not without problems, but when I found C I threw Pascal under the nearest bus and never wrote a line in it again. The taste was so bad, I refused to even look at Modula II and its failed successors. D has 12 integral types. Disabling implicit integral conversions would make it unbearable to use.

Oh, I absolutely agree that implicit conversions are good in certain cases. I was only referring to implicit conversions between enums and the enum's base type (regardless of direction).
Mar 25 2010
prev sibling next sibling parent reply yigal chripun <yigal100 gmail.com> writes:
Walter Bright Wrote:

 Nick Sabalausky wrote:
 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".

You're certainly not the first to feel this way about implicit conversions. Niklaus Wirth did the same, and designed Pascal with no implicit conversions. You had to do an explicit cast each time. Man, what a royal pain in the ass that makes coding in Pascal. Straightforward coding, like converting a string of digits to an integer, becomes a mess of casts. Even worse, casts are a blunt instrument that *destroys* type checking (that wasn't so much of a problem with Pascal with its stone age abstract types, but it would be killer for D). Implicit integral conversions are not without problems, but when I found C I threw Pascal under the nearest bus and never wrote a line in it again. The taste was so bad, I refused to even look at Modula II and its failed successors. D has 12 integral types. Disabling implicit integral conversions would make it unbearable to use.

here's a simple version without casts: int toString(dchar[] arr) { int temp = 0; for (int i = 0; i < arr.length; i++) { int digit = arr[i].valueOf - 30; // * if (digit < 0 || digit > 9) break; temp += 10^^i * digit; } return temp; } [*] Assume that dchar has a valueOf property that returns the value. where's that mess of casts you mention? Pascal is hardly the only language without excplicit casts. ML is also properly strongly typed and is an awesome language to use. The fact that D has 12 integral types is a bad design, why do we need so many built in types? to me this clearly shows a need to refactor this aspect of D.
Mar 25 2010
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
yigal chripun wrote:
 here's a simple version without casts: int toString(dchar[] arr) { int temp =
 0; for (int i = 0; i < arr.length; i++) { int digit = arr[i].valueOf - 30; //
 * if (digit < 0 || digit > 9) break; temp += 10^^i * digit; } return temp; }
 
 [*] Assume that dchar has a valueOf property that returns the value.
 
 where's that mess of casts you mention?

In Pascal, you'd have type errors all over the place. First off, you cannot do arithmetic on characters. You have to cast them to integers (with the ORD(c) construction).
 Pascal is hardly the only language without excplicit casts.

Pascal has explicit casts. The integer to character one is CHR(i), the character to integer is ORD(c).
 ML is also
 properly strongly typed and is an awesome language to use.

I don't know enough about ML to comment intelligently on it.
 The fact that D has 12 integral types is a bad design, why do we need so many
 built in types? to me this clearly shows a need to refactor this aspect of D.

Which would you get rid of? (13, I forgot bool!) bool byte ubyte short ushort int uint long ulong char wchar dchar enum
Mar 25 2010
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Walter Bright Wrote:

 yigal chripun wrote:
 here's a simple version without casts: int toString(dchar[] arr) { int temp =
 0; for (int i = 0; i < arr.length; i++) { int digit = arr[i].valueOf - 30; //
 * if (digit < 0 || digit > 9) break; temp += 10^^i * digit; } return temp; }
 
 [*] Assume that dchar has a valueOf property that returns the value.
 
 where's that mess of casts you mention?

In Pascal, you'd have type errors all over the place. First off, you cannot do arithmetic on characters. You have to cast them to integers (with the ORD(c) construction).

 Pascal is hardly the only language without excplicit casts.

Pascal has explicit casts. The integer to character one is CHR(i), the character to integer is ORD(c).

I meant implicit, sorry about that. The pascal way is definitely the correct way. what's the semantics in your opinion of ('f' + 3) ? what about ('?' + 4)? making such arithmetic valid is wrong. I'm sure that the first Pascal versions had problems which caused you to ditch that language (they where fixed later). I doubt it though that this had a large impact on Pascal's problems.
 
 ML is also
 properly strongly typed and is an awesome language to use.

I don't know enough about ML to comment intelligently on it.
 The fact that D has 12 integral types is a bad design, why do we need so many
 built in types? to me this clearly shows a need to refactor this aspect of D.

Which would you get rid of? (13, I forgot bool!) bool byte ubyte short ushort int uint long ulong char wchar dchar enum

you forgot the cent and ucent types and what about 256bit types? Here's How I'd want it designed: First of, a Boolean type should not belong to this list at all and shouldn't be treated as a numeric type. Second, there really only few use-cases that are relevant signed types for representing numbers: 1) unlimited integral type - int 2) limited integral type - int!(bits), e.g. int!16, int!8, etc.. 3) user defined range: e.g. [0, infinity) for positive numbers, etc.. unsigned bit-packs: 4) bits!(size), e.g. bits!8, bits!32, etc.. of course you can define useful aliases, e.g. alias bits!8 Byte; alias bits!16 Word; .. or you can define the aliases per the architecture, so that Word above will be defined for the current arch (I don't know what's the native word size on say ARM and other platforms) char and relatives should be for text only per Unicode, (perhaps a better name is code-point). for other encodings use the above bit packs, e.g. alias bits!7 Ascii; alias bits!8 ExtendedAscii; etc.. enum should be an enumeration type. You can find an excellent strongly-typed design in Java 5.0
Mar 25 2010
next sibling parent BCS <none anon.com> writes:
Hello Yigal,

 I meant implicit, sorry about that. The pascal way is definitely the
 correct way. 

Wrong. It is not "definitely" the correct way because there are many smart people who disagree. It might *be* the correct way, but it that has yet to be shown.
 what's the semantics in your opinion of ('f' + 3) ? what
 about ('?' + 4)? making such arithmetic valid is wrong.

The above will depend on the underlying system (because we are talking D, that would be UTF). The first will result in the value three grater than the value used to represent 'f' (in UTF, 'i'). The same happens for the second with suitable substations.
 The fact that D has 12 integral types is a bad design, why do we
 need so many built in types? to me this clearly shows a need to
 refactor this aspect of D.
 

bool byte ubyte short ushort int uint long ulong char wchar dchar enum

you forgot the cent and ucent types and what about 256bit types? Here's How I'd want it designed: First of, a Boolean type should not belong to this list at all and shouldn't be treated as a numeric type.

Agree, and it isn't for the most part.
 
 Second, there really only few use-cases that are relevant
 
 signed types for representing numbers:
 1) unlimited integral type - int

Very rare and expensive. This, IMHO, should not be a built in type
 3) user defined range: e.g. [0, infinity) for positive numbers, etc..

Also rare and expensive for most uses. Again, not built in. [reordered a bit]
 2) limited integral type  - int!(bits), e.g. int!16, int!8, etc..
 unsigned bit-packs:
 4) bits!(size), e.g. bits!8, bits!32, etc..

This would make it look like int!13 and int!16 are both valid and similarly costly. If you do make int!13 valid how will it pack in arrays? (Hint: if it doesn't pack, I see no more value in it than in a range type, see comment to item 3) Will you be able to take a pointer to any element? (Hint: the problems with that are why D dropped the bit type.) When you get right down to it, the types supported by hardware are fairly standard (8,16,32,64,... bits signed and unsigned) so why shouldn't the language support them directly? I will grant that there are reasons to do what you are suggesting, but I don't think they are valid reason with regards to the language the D is trying to be.
 of course you can define useful aliases, e.g.
 alias bits!8 Byte;
 alias bits!16 Word;
 ..
 or you can define the aliases per the architecture, so that Word above
 will be defined for the current arch (I don't know what's the native
 word size on say ARM and other platforms)

C more or less has that with int. That D dropped the "what size is int" problem is a good thing in my book.
 char and relatives should be for text only per Unicode, (perhaps a
 better name is code-point). for other encodings use the above bit
 packs, e.g.
 
 alias bits!7 Ascii;
 
 alias bits!8 ExtendedAscii;
 
 etc..

D defines text to be unicode. Unicode is a sequence of numbers. It's all well defined and whatnot so allowing the programmer to handle them as numbers is a must as far as I'm concerned. Finally, none of that part about integers has anything to do with implicit casts. If anything, it would make it harder as you now have even more types, not less.
 
 enum should be an enumeration type. You can find an excellent
 strongly-typed  design in Java 5.0
 

... <IXOYE><
Mar 25 2010
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Yigal Chripun wrote:
 Walter Bright Wrote:
 Pascal has explicit casts. The integer to character one is CHR(i), the
 character to integer is ORD(c).

way. what's the semantics in your opinion of ('f' + 3) ? what about ('?' + 4)? making such arithmetic valid is wrong.

Yes, that is exactly the opinion of Pascal. As I said, I've programmed in Pascal, suffered as it blasted my kingdom, and I don't wish to do that again. I see no use in pretending '?' does not have a numerical value that is very useful to manipulate.
 I'm sure that the first Pascal
 versions had problems which caused you to ditch that language (they where
 fixed later).

They weren't compiler bugs I was wrestling with. They were fundamental design decisions of the language.
 I doubt it though that this had a large impact on Pascal's
 problems.

I don't agree. Pascal was a useless language as designed. This meant that every vendor added many incompatible extensions. Anyone who used Pascal got locked into a particular vendor. That killed it.
 The fact that D has 12 integral types is a bad design, why do we need so
 many built in types? to me this clearly shows a need to refactor this
 aspect of D.

bool byte ubyte short ushort int uint long ulong char wchar dchar enum

you forgot the cent and ucent types and what about 256bit types?

They are reserved, not implemented, so I left them out. In or out, they don't change the point.
 Here's How I'd want it designed: First of, a Boolean type should not belong
 to this list at all and shouldn't be treated as a numeric type. Second, there
 really only few use-cases that are relevant
 
 signed types for representing numbers: 1) unlimited integral type - int 2)
 limited integral type  - int!(bits), e.g. int!16, int!8, etc.. 3) user
 defined range: e.g. [0, infinity) for positive numbers, etc..
 
 unsigned bit-packs: 4) bits!(size), e.g. bits!8, bits!32, etc..
 
 of course you can define useful aliases, e.g. alias bits!8 Byte; alias
 bits!16 Word; .. or you can define the aliases per the architecture, so that
 Word above will be defined for the current arch (I don't know what's the
 native word size on say ARM and other platforms)

People are going to quickly tire of writing: bits!8 b; bits!16 s; and are going to use aliases: alias bits!8 ubyte; alias bits!16 ushort; Naturally, either everyone invents their own aliases (like they do in C with its indeterminate int sizes), or they are standardized, in which case we're back to pretty much exactly the same point we are at now. I don't see where anything was accomplished.
 char and relatives should be for text only per Unicode, (perhaps a better
 name is code-point).

There have been many proposals to try and hide the fact that UTF-8 is really a multibyte encoding, but that makes for some pretty inefficient code in too many cases.
 for other encodings use the above bit packs, e.g. alias
 bits!7 Ascii; alias bits!8 ExtendedAscii; etc..
 
 enum should be an enumeration type. You can find an excellent strongly-typed
 design in Java 5.0

Those enums are far more heavyweight - they are a syntactic sugar around a class type complete with methods, interfaces, constructors, etc. They aren't even compile time constants! If you need those in D, it wouldn't be hard at all to make a library class template that does the same thing.
Mar 25 2010
parent reply yigal chripun <yigal100 gmail.com> writes:
Walter Bright Wrote:

 Yigal Chripun wrote:
 Walter Bright Wrote:
 Pascal has explicit casts. The integer to character one is CHR(i), the
 character to integer is ORD(c).

way. what's the semantics in your opinion of ('f' + 3) ? what about ('?' + 4)? making such arithmetic valid is wrong.

Yes, that is exactly the opinion of Pascal. As I said, I've programmed in Pascal, suffered as it blasted my kingdom, and I don't wish to do that again. I see no use in pretending '?' does not have a numerical value that is very useful to manipulate.

'?' indeed does *not* have a single numerical value that identiies it in a unique manner. You can map it to different numeric values based on encoding and even within the same encoding this doesn't always hold. See normalization in Unicode for different encodings for the same character.
 I'm sure that the first Pascal
 versions had problems which caused you to ditch that language (they where
 fixed later).

They weren't compiler bugs I was wrestling with. They were fundamental design decisions of the language.
 I doubt it though that this had a large impact on Pascal's
 problems.

I don't agree. Pascal was a useless language as designed. This meant that every vendor added many incompatible extensions. Anyone who used Pascal got locked into a particular vendor. That killed it.
 The fact that D has 12 integral types is a bad design, why do we need so
 many built in types? to me this clearly shows a need to refactor this
 aspect of D.

bool byte ubyte short ushort int uint long ulong char wchar dchar enum

you forgot the cent and ucent types and what about 256bit types?

They are reserved, not implemented, so I left them out. In or out, they don't change the point.
 Here's How I'd want it designed: First of, a Boolean type should not belong
 to this list at all and shouldn't be treated as a numeric type. Second, there
 really only few use-cases that are relevant
 
 signed types for representing numbers: 1) unlimited integral type - int 2)
 limited integral type  - int!(bits), e.g. int!16, int!8, etc.. 3) user
 defined range: e.g. [0, infinity) for positive numbers, etc..
 
 unsigned bit-packs: 4) bits!(size), e.g. bits!8, bits!32, etc..
 
 of course you can define useful aliases, e.g. alias bits!8 Byte; alias
 bits!16 Word; .. or you can define the aliases per the architecture, so that
 Word above will be defined for the current arch (I don't know what's the
 native word size on say ARM and other platforms)

People are going to quickly tire of writing: bits!8 b; bits!16 s; and are going to use aliases: alias bits!8 ubyte; alias bits!16 ushort; Naturally, either everyone invents their own aliases (like they do in C with its indeterminate int sizes), or they are standardized, in which case we're back to pretty much exactly the same point we are at now. I don't see where anything was accomplished.

what if new hardware adds support for larger vector ops and 512bit registers, will we now need to extend the language with another type? On the flip side of this, programmers almost always will need just an int since they need the mathematical notion of an integral type. Iit's prtty rare when programmers want something other than int and in those cases they'll define thir own types anyway since they know what their requirements are.
 
 char and relatives should be for text only per Unicode, (perhaps a better
 name is code-point).

There have been many proposals to try and hide the fact that UTF-8 is really a multibyte encoding, but that makes for some pretty inefficient code in too many cases.

I'm not saying we should hide that, on the contrary, the compiler should enforce unicode and other encodings should use a bits type instead. a [w|d]char must always contain a valid unicode value. calling char[] a string is wrong since it is actually an array of code-points which is not always a valid encoding. a dchar[] is however a valid string since each individual dchar contains a full code-unit.
 
 for other encodings use the above bit packs, e.g. alias
 bits!7 Ascii; alias bits!8 ExtendedAscii; etc..
 
 enum should be an enumeration type. You can find an excellent strongly-typed
 design in Java 5.0

Those enums are far more heavyweight - they are a syntactic sugar around a class type complete with methods, interfaces, constructors, etc. They aren't even compile time constants! If you need those in D, it wouldn't be hard at all to make a library class template that does the same thing.

They aren't that heavy weight. Instead of assigning an int to each symbol you assign a pointer address which is the same size. Regarding the compile time property: for an int type: const int a = 5; //compile time the same should apply to enums as well. The problem with the library solution is that it can't provide the syntax sugar for this.
Mar 25 2010
parent reply Walter Bright <newshound1 digitalmars.com> writes:
yigal chripun wrote:
 Walter Bright Wrote:
 
 Yigal Chripun wrote:
 Walter Bright Wrote:
 Pascal has explicit casts. The integer to character one is CHR(i), the 
 character to integer is ORD(c).

correct way. what's the semantics in your opinion of ('f' + 3) ? what about ('?' + 4)? making such arithmetic valid is wrong.

Pascal, suffered as it blasted my kingdom, and I don't wish to do that again. I see no use in pretending '?' does not have a numerical value that is very useful to manipulate.

'?' indeed does *not* have a single numerical value that identiies it in a unique manner. You can map it to different numeric values based on encoding and even within the same encoding this doesn't always hold. See normalization in Unicode for different encodings for the same character.

That's true, '?' can have different encodings, such as for EBCDIC and RADIX50. Those formats are dead, however, and ASCII has won. D is specifically a Unicode language (a superset of ASCII) and '?' has a single defined value for it. Yes, Unicode has some oddities about it, and the poor programmer using those characters will have to deal with it, but that does not change that quoted character literals are always the same numerical value. '?' is not going to change to another one tomorrow or in any conceivable future incarnation of Unicode.
 Naturally, either everyone invents their own aliases (like they do in C
 with its indeterminate int sizes), or they are standardized, in which case
 we're back to pretty much exactly the same point we are at now. I don't see
 where anything was accomplished.
 

bits!24. How would I do that in current D?

You'd be on your own with that. I had a discussion recently with a person who defended C's notion of compiler defined integer sizes, pointing out that this enabled compliant C compilers to be written for DSLs with 32 bit bytes. That is pedantically correct, compliant C compilers were written for it. Unfortunately, practically no C applications could be ported to it without extensive modification! For your 24 bit machine, you will be forced to write all your own custom software, even if the D specification supported it.
 what if new hardware adds support
 for larger vector ops and 512bit registers, will we now need to extend the
 language with another type?

D will do something to accommodate it, obviously we don't know what that will be until we see what those types are and what they do. What I don't see is using 512 bit ints for normal use.
 char and relatives should be for text only per Unicode, (perhaps a better
  name is code-point).

really a multibyte encoding, but that makes for some pretty inefficient code in too many cases.

I'm not saying we should hide that, on the contrary, the compiler should enforce unicode and other encodings should use a bits type instead. a [w|d]char must always contain a valid unicode value. calling char[] a string is wrong since it is actually an array of code-points which is not always a valid encoding. a dchar[] is however a valid string since each individual dchar contains a full code-unit.

Conceptually, I agree, it's wrong, but it's not practical to force the issue.
 enum should be an enumeration type. You can find an excellent
 strongly-typed design in Java 5.0

class type complete with methods, interfaces, constructors, etc. They aren't even compile time constants! If you need those in D, it wouldn't be hard at all to make a library class template that does the same thing.

They aren't that heavy weight. Instead of assigning an int to each symbol you assign a pointer address which is the same size.

No, it's not the same. A compile time constant has many advantages over a runtime one. Java creates an inner class for each enum member, not just the enum itself! It's heavyweight.
 Regarding the compile time
 property: for an int type: const int a = 5; //compile time the same should
 apply to enums as well.

Having a runtime pointer to an inner class for each enum value is far from the advantages of a compile time constant.
 The problem with the library solution is that it can't provide the syntax
 sugar for this.

It can get pretty close. Java has poor abstraction facilities, and so building it into the language was the only solution.
Mar 25 2010
parent reply yigal chripun <yigal100 gmail.com> writes:
It seems that on a conceptual level we are in complete agreement. 
the difference seems to be that you want to push some things onto the user
which I think the language should provide.

Walter Bright Wrote:

 yigal chripun wrote:
 Walter Bright Wrote:
 
 Yigal Chripun wrote:
 Walter Bright Wrote:
 Pascal has explicit casts. The integer to character one is CHR(i), the 
 character to integer is ORD(c).

correct way. what's the semantics in your opinion of ('f' + 3) ? what about ('?' + 4)? making such arithmetic valid is wrong.

Pascal, suffered as it blasted my kingdom, and I don't wish to do that again. I see no use in pretending '?' does not have a numerical value that is very useful to manipulate.

'?' indeed does *not* have a single numerical value that identiies it in a unique manner. You can map it to different numeric values based on encoding and even within the same encoding this doesn't always hold. See normalization in Unicode for different encodings for the same character.

That's true, '?' can have different encodings, such as for EBCDIC and RADIX50. Those formats are dead, however, and ASCII has won. D is specifically a Unicode language (a superset of ASCII) and '?' has a single defined value for it. Yes, Unicode has some oddities about it, and the poor programmer using those characters will have to deal with it, but that does not change that quoted character literals are always the same numerical value. '?' is not going to change to another one tomorrow or in any conceivable future incarnation of Unicode.

while it's true that '?' has one unicode value for it, it's not true for all sorts of diacritics and combine code-points. So your approach is to pass the responsibility for that to the end user which in 99.9999% will not handle this correctlly.
 Naturally, either everyone invents their own aliases (like they do in C
 with its indeterminate int sizes), or they are standardized, in which case
 we're back to pretty much exactly the same point we are at now. I don't see
 where anything was accomplished.
 

bits!24. How would I do that in current D?

You'd be on your own with that. I had a discussion recently with a person who defended C's notion of compiler defined integer sizes, pointing out that this enabled compliant C compilers to be written for DSLs with 32 bit bytes. That is pedantically correct, compliant C compilers were written for it. Unfortunately, practically no C applications could be ported to it without extensive modification! For your 24 bit machine, you will be forced to write all your own custom software, even if the D specification supported it.

Of course any software that depends on a specifc size, e.g. bits!32 will need to be extensively modified if it's ported to an arch which requires a different size. But I'm talking about the need to define bits!(T) myself instead of having it in the standard library.
 
 what if new hardware adds support
 for larger vector ops and 512bit registers, will we now need to extend the
 language with another type?

D will do something to accommodate it, obviously we don't know what that will be until we see what those types are and what they do. What I don't see is using 512 bit ints for normal use.

There's another issue here and that's all those types are special cases in the compiler and handled separately from library types. Had the stdlib provided the templeted types it would have allowed to use them in more generic ways instead of special caseing them everywhere.
 
 
 char and relatives should be for text only per Unicode, (perhaps a better
  name is code-point).

really a multibyte encoding, but that makes for some pretty inefficient code in too many cases.

I'm not saying we should hide that, on the contrary, the compiler should enforce unicode and other encodings should use a bits type instead. a [w|d]char must always contain a valid unicode value. calling char[] a string is wrong since it is actually an array of code-points which is not always a valid encoding. a dchar[] is however a valid string since each individual dchar contains a full code-unit.

Conceptually, I agree, it's wrong, but it's not practical to force the issue.

 
 
 enum should be an enumeration type. You can find an excellent
 strongly-typed design in Java 5.0

class type complete with methods, interfaces, constructors, etc. They aren't even compile time constants! If you need those in D, it wouldn't be hard at all to make a library class template that does the same thing.

They aren't that heavy weight. Instead of assigning an int to each symbol you assign a pointer address which is the same size.

No, it's not the same. A compile time constant has many advantages over a runtime one. Java creates an inner class for each enum member, not just the enum itself! It's heavyweight.
 Regarding the compile time
 property: for an int type: const int a = 5; //compile time the same should
 apply to enums as well.

Having a runtime pointer to an inner class for each enum value is far from the advantages of a compile time constant.

 
 The problem with the library solution is that it can't provide the syntax
 sugar for this.

It can get pretty close. Java has poor abstraction facilities, and so building it into the language was the only solution.

how close can it get? I don't mind having this in the stdlib instead of in the language if it's pleasent enough on the eyes.
Mar 25 2010
parent reply KennyTM~ <kennytm gmail.com> writes:
On Mar 26, 10 05:46, yigal chripun wrote:
 while it's true that '?' has one unicode value for it, it's not true for all
sorts of diacritics and combine code-points. So your approach is to pass the
responsibility for that to the end user which in 99.9999% will not handle this
correctlly.

Non-issue. Since when can a character literal store > 1 code-point?
Mar 25 2010
parent reply yigal chripun <yigal100 gmail.com> writes:
KennyTM~ Wrote:

 On Mar 26, 10 05:46, yigal chripun wrote:
 while it's true that '?' has one unicode value for it, it's not true for all
sorts of diacritics and combine code-points. So your approach is to pass the
responsibility for that to the end user which in 99.9999% will not handle this
correctlly.

Non-issue. Since when can a character literal store > 1 code-point?

character != code-point D chars are really as you say code-points and not always complete characters. here's a use case for you: you want to write a fully unicode aware search engine. If you just try to match the given sequnce of code-points in the search term, you will miss valid matches since, for instance you do not take into account permutations of the order of combining marks. you can't just assume that the code-point value identifies the character.
Mar 26 2010
parent reply KennyTM~ <kennytm gmail.com> writes:
On Mar 26, 10 18:52, yigal chripun wrote:
 KennyTM~ Wrote:

 On Mar 26, 10 05:46, yigal chripun wrote:
 while it's true that '?' has one unicode value for it, it's not true for all
sorts of diacritics and combine code-points. So your approach is to pass the
responsibility for that to the end user which in 99.9999% will not handle this
correctlly.

Non-issue. Since when can a character literal store> 1 code-point?

character != code-point D chars are really as you say code-points and not always complete characters. here's a use case for you: you want to write a fully unicode aware search engine. If you just try to match the given sequnce of code-points in the search term, you will miss valid matches since, for instance you do not take into account permutations of the order of combining marks. you can't just assume that the code-point value identifies the character.

Stop being off-topic. '?' is of type char, not string. A char always holds an octet of UTF-8 encoded sequence. The numerical content is unique and well-defined*. Therefore adding 4 to '?' also has a meaning. * If you're paranoid you may request the spec to ensure the character is in NFC form.
Mar 26 2010
parent reply Yigal Chripun <yigal100 gmail.com> writes:
KennyTM~ Wrote:

 On Mar 26, 10 18:52, yigal chripun wrote:
 KennyTM~ Wrote:

 On Mar 26, 10 05:46, yigal chripun wrote:
 while it's true that '?' has one unicode value for it, it's not true for all
sorts of diacritics and combine code-points. So your approach is to pass the
responsibility for that to the end user which in 99.9999% will not handle this
correctlly.

Non-issue. Since when can a character literal store> 1 code-point?

character != code-point D chars are really as you say code-points and not always complete characters. here's a use case for you: you want to write a fully unicode aware search engine. If you just try to match the given sequnce of code-points in the search term, you will miss valid matches since, for instance you do not take into account permutations of the order of combining marks. you can't just assume that the code-point value identifies the character.

Stop being off-topic. '?' is of type char, not string. A char always holds an octet of UTF-8 encoded sequence. The numerical content is unique and well-defined*. Therefore adding 4 to '?' also has a meaning. * If you're paranoid you may request the spec to ensure the character is in NFC form.

Huh? You jump in in the middle of conversation and I'm off-topic? Now, to get back to the topic at hand: D's current design is: char/dchar/wchar are integral types that can contain any value/encoding even though D prefers Unicode. This is not enforced. e.g. you can have a valid wchar which you increment by 1 and get an invalid wchar. Instead, Let's have proper well defined semantics in D: Design A: char/wchar/dchar are defined to be Unicode code-points for the respective encodings. These is enforces by the language so if you want to define a different encoding you must use something like bits!8 arithmetic on code-points is defined according to the Unicode standard. Design B: char represents a (perhaps multi-byte) character. Arithmetic on this type is *not* defined. In either case these types should not be treated as plain integral types.
Mar 28 2010
parent KennyTM~ <kennytm gmail.com> writes:
On Mar 28, 10 18:56, Yigal Chripun wrote:
 KennyTM~ Wrote:

 On Mar 26, 10 18:52, yigal chripun wrote:
 KennyTM~ Wrote:

 On Mar 26, 10 05:46, yigal chripun wrote:
 while it's true that '?' has one unicode value for it, it's not true for all
sorts of diacritics and combine code-points. So your approach is to pass the
responsibility for that to the end user which in 99.9999% will not handle this
correctlly.

Non-issue. Since when can a character literal store> 1 code-point?

character != code-point D chars are really as you say code-points and not always complete characters. here's a use case for you: you want to write a fully unicode aware search engine. If you just try to match the given sequnce of code-points in the search term, you will miss valid matches since, for instance you do not take into account permutations of the order of combining marks. you can't just assume that the code-point value identifies the character.

Stop being off-topic. '?' is of type char, not string. A char always holds an octet of UTF-8 encoded sequence. The numerical content is unique and well-defined*. Therefore adding 4 to '?' also has a meaning. * If you're paranoid you may request the spec to ensure the character is in NFC form.

Huh? You jump in in the middle of conversation and I'm off-topic?

Yes. The original discussion is on implicit conversion, which leads to whether ('x' + 1) is semantically correct. How will this be related to search engine? (Technically even this is off-topic. The title said implicit *enum* conversion.)
 Now, to get back to the topic at hand:

 D's current design is:
 char/dchar/wchar are integral types that can contain any value/encoding even
though D prefers Unicode. This is not enforced.
 e.g. you can have a valid wchar which you increment by 1 and get an invalid
wchar.

Wrong. Read the specs: http://digitalmars.com/d/1.0/type.html, http://digitalmars.com/d/2.0/type.html * char = unsigned 8 bit UTF-8 * wchar = unsigned 16 bit UTF-16 * dchar = unsigned 32 bit UTF-32 To contain any encoding, use ubyte.
 Instead, Let's have proper well defined semantics in D:

 Design A:
 char/wchar/dchar are defined to be Unicode code-points for the respective
encodings. These is enforces by the language so if you want to define a
different encoding you must use something like bits!8
 arithmetic on code-points is defined according to the Unicode  standard.

 Design B:
 char represents a (perhaps multi-byte) character.
 Arithmetic on this type is *not* defined.

 In either case these types should not be treated as plain integral types.

Mar 28 2010
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
yigal chripun:
 The fact that D has 12 integral types is a bad design, why do we need so many
built in types? to me this clearly shows a need to refactor this aspect of D. 

In a system language you need them all, and you need multi-precision integers too. Note: C# too has unsigned types, but they are separated :-) Bye, bearophile
Mar 25 2010
prev sibling next sibling parent reply Don <nospam nospam.com> writes:
Walter Bright wrote:
 Nick Sabalausky wrote:
 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".

You're certainly not the first to feel this way about implicit conversions. Niklaus Wirth did the same, and designed Pascal with no implicit conversions. You had to do an explicit cast each time. Man, what a royal pain in the ass that makes coding in Pascal. Straightforward coding, like converting a string of digits to an integer, becomes a mess of casts. Even worse, casts are a blunt instrument that *destroys* type checking (that wasn't so much of a problem with Pascal with its stone age abstract types, but it would be killer for D). Implicit integral conversions are not without problems, but when I found C I threw Pascal under the nearest bus and never wrote a line in it again. The taste was so bad, I refused to even look at Modula II and its failed successors. D has 12 integral types. Disabling implicit integral conversions would make it unbearable to use.

I think there might be some low-hanging fruit, though. Supposed we distinguished enums containing AssignExpressions from those which do not. It seems clear to me that logical operations should always be permitted on enums where every member of the enum has been explicitly assigned a value. enum Enum1 { A = 1, B = 2, C = 4 } ---> A|B makes sense. But if there are no assign expressions at all: enum Enum2 { A, B, C } then I think that performing arithmetic on that enum is almost certainly a bug. The case where only some of the enum members have assign expressions is less clear. Some cases, such as synonyms { A, B, C=B } aren't really any different from the no-assign expression case. But other cases are less clear, so I'll just ignore them all. So I propose a simple rule: Suppose that implicit integral conversions (and arithmetic/logical operations) involving enums were permitted ONLY if the enum has at least one AssignExpression. This would catch some bugs, without (I think) causing much pain for valid code. At the very least, this is something I'd want in a lint tool. (But note that it wouldn't fix the issue in the original post).
Mar 25 2010
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Don wrote:
 This would catch some bugs, without (I think) causing much pain for 
 valid code. At the very least, this is something I'd want in a lint 
 tool. (But note that it wouldn't fix the issue in the original post).

Such rules are interesting, but I worry that they are both too clever and too obtuse. Having a lot of such rules may make using D very frustrating because you'll never know when you'll get hit by one of them. I'd rather have a simpler, more orthogonal, and easier to remember rules that may have a downside here and there rather than a complex web of seemingly arbitrary ones.
Mar 25 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Walter Bright:
 Such rules are interesting, but I worry that they are both too clever and too 
 obtuse. Having a lot of such rules may make using D very frustrating because 
 you'll never know when you'll get hit by one of them.
 
 I'd rather have a simpler, more orthogonal, and easier to remember rules that 
 may have a downside here and there rather than a complex web of seemingly 
 arbitrary ones.

I agree a lot, and I agree Don was wrong here. In my posts I have already shown a better solution, that's similar to the C# one: enum Foo {...} // normal enum, no implicit conversions, no operators flags enum Foo {...} // no implicit conversions, boolean operator plus "in". Bye, bearophile
Mar 25 2010
prev sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Don" <nospam nospam.com> wrote in message 
news:hof6fe$6ah$1 digitalmars.com...
 I think there might be some low-hanging fruit, though.
 Supposed we distinguished enums containing AssignExpressions from those 
 which do not.
 It seems clear to me that logical operations should always be permitted on 
 enums where every member of the enum has been explicitly assigned a value.
 enum Enum1 { A = 1, B = 2, C = 4 }
  ---> A|B makes sense.

 But if there are no assign expressions at all:
 enum Enum2 { A, B, C }
 then I think that performing arithmetic on that enum is almost certainly a 
 bug.

 The case where only some of the enum members have assign expressions is 
 less clear. Some cases, such as synonyms { A, B, C=B } aren't really any 
 different from the no-assign expression case. But other cases are less 
 clear, so I'll just ignore them all. So I propose a simple rule:

 Suppose that implicit integral conversions (and arithmetic/logical 
 operations) involving enums were permitted ONLY if the enum has at least 
 one AssignExpression.

 This would catch some bugs, without (I think) causing much pain for valid 
 code. At the very least, this is something I'd want in a lint tool. (But 
 note that it wouldn't fix the issue in the original post).

If I understand that right, I think that side-steps what I see as the important goal: To *have* an associated value with each enum member but just have to be explicit about obtaining it. The above scheme doesn't really bring things significantly closer to that, you have to make a choice between type-safety and being able actually have an underlying value at all. So when you need an associated value but you're not doing bitfields/flags (which is a situation I find to be very common for enums), then that solution provides no improvement over the current state. Here's the low-hanging fruit I see: Step 1: Remove implicit enum->base-type conversions Step 2: Allow '|' (and maybe '&'?) on enums, and consider the result of the operation be the base type. Certainly not ideal, but it provides far more type-safety than we currently have on non-bitfield/non-flag enums, even if you're using associated values, and still allows enums to be used for bitfields/flags without any code breakage or ugly "cast(int)Foo.a | cast(int)Foo.b".
Mar 25 2010
parent reply Regan Heath <regan netmail.co.nz> writes:
Nick Sabalausky wrote:
 Here's the low-hanging fruit I see:
 
 Step 1: Remove implicit enum->base-type conversions
 Step 2: Allow '|' (and maybe '&'?) on enums, and consider the result of the 
 operation be the base type.

I would prefer the result of Step 2 to be the enum type, not the base type(*) The typical case I see for enums as flags is passing a combination of them to a function, where the parameter (for type-safety & self-documentation) will probably be of the enum type. However, it occurs to me we don't actually get full type safety unless we also ensure the enum never has a value outside those defined, or a combination thereof(*) Example: enum Flags { A = 0x1, B = 0x2, C = 0x4, D = 0x8 } // // possible values for a variable of type 'Flags' // 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA // 0xB, 0xC, 0xD, 0xE, 0xF // // values < 0x1 and > 0xF are illegal // // default value of variable of type 'Flags' is 0x1 (A) // <incomplete code snippets..> void foo(Flags f) {} //accepts only legal values for Flags foo(Flags.A|Flags.B); //ok foo(0x10); //error Flags f; //ok, f = 0x1 Flags err = 0x10; //error foo(Flags.A|0x10); //error </incomplete code snippets..> (*) Some operators would return the base type, take ~ for example, it naturally produces a value outside the range of values defined by the enum type. It follows that operators like |[=], &[=] etc would need to accept enum and base types as operands, otherwise some common idioms would be illegal, eg. Flags f = Flags.A|Flags.B|Flags.C; f &= ~Flags.A; // remove Flags.A from 'f' R
Mar 25 2010
parent "Nick Sabalausky" <a a.a> writes:
"Regan Heath" <regan netmail.co.nz> wrote in message 
news:hogaop$2kp2$1 digitalmars.com...
 Nick Sabalausky wrote:
 Here's the low-hanging fruit I see:

 Step 1: Remove implicit enum->base-type conversions
 Step 2: Allow '|' (and maybe '&'?) on enums, and consider the result of 
 the operation be the base type.

I would prefer the result of Step 2 to be the enum type, not the base type(*)

Agreed, but to do that correctly, the compiler would have to be able to distinguish between flag/bitfield-type enums and other enums, because many enums are *not* intended to be combinable and trying to do so should be an error. That's why I suggested the above as a low-hanging-fruit compromise. But yea, if Walter were fine with taking it further and having that proper separation of flag/bitfield enums and non-flag/non-bitfield enums, then all the better.
Mar 25 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Don <nospam nospam.com> wrote:

 I think there might be some low-hanging fruit, though.
 Supposed we distinguished enums containing AssignExpressions from those  
 which do not.
 It seems clear to me that logical operations should always be permitted  
 on enums where every member of the enum has been explicitly assigned a  
 value.
 enum Enum1 { A = 1, B = 2, C = 4 }
   ---> A|B makes sense.

 But if there are no assign expressions at all:
 enum Enum2 { A, B, C }
 then I think that performing arithmetic on that enum is almost certainly  
 a bug.

I wonder, what if the default base type of an enum was simply 'enum', a type not implicitly convertible to other types. If you want implicit casts, specify the base type: enum foo { A, B, C = B } // No base type, no conversions allowed. enum bar : int { D, E, F = E } // An int in disguise. Allow conversions. That seems to follow D's tenet that the unsafe should be more verbose. -- Simen
Mar 25 2010
prev sibling next sibling parent reply grauzone <none example.net> writes:
Walter Bright wrote:
 Nick Sabalausky wrote:
 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".

You're certainly not the first to feel this way about implicit conversions. Niklaus Wirth did the same, and designed Pascal with no implicit conversions. You had to do an explicit cast each time. Man, what a royal pain in the ass that makes coding in Pascal. Straightforward coding, like converting a string of digits to an integer, becomes a mess of casts. Even worse, casts are a blunt instrument that *destroys* type checking (that wasn't so much of a problem with Pascal with its stone age abstract types, but it would be killer for D).

That's funny that you're saying this. Casts are totally messed up in D. Some casts do safe operations (casts between objects and interfaces), some are absolutely ridiculous and only useful in low level situations (casting array slices), some are safe whenever the compiler feels like it (array casts of array literals versus array slices), and some fundamentally break other language features, even accidentally (immutable). casts are easily grepable, but you never know what a specific cast actually does. Think about what this means for generic templated code. In summary, I'd say casting rules in D are the worst spawn of hell. I mean, that's ok, it doesn't exactly make D useless. And you can always introduce your own safe (exe bloating, sigh) casting template functions. But I still find it funny that you say this.
 Implicit integral conversions are not without problems, but when I found 
 C I threw Pascal under the nearest bus and never wrote a line in it 
 again. The taste was so bad, I refused to even look at Modula II and its 
 failed successors.

For your information, programming in Delphi (modern Pascal dialect) was quite a joy. It combined the advantages of the low level programming of C (pointers, inline assembler), was safer than C, and included a sane object model similar to Java/C#. Sounds familiar? (Yeah, the template fetishists must have been very unhappy with it.) I really don't understand why you're bashing Pascal at large. You must have had only experience with early Pascal dialects... and then never looked at anything that smelled like Pascal... and this as a language designer??
 D has 12 integral types. Disabling implicit integral conversions would 
 make it unbearable to use.

PS: while you guys are talking about new "absolutely necessary" language features, people new to D are despairing to get a working D installation and *trying* to use external libraries from dsource (that kind of struggling really isn't nice to watch), and people "old" to D are desparing over compiler regressions and random bugs that have global effects on middle-sized to large codebases (circular dependency and optlink bugs come to mind). The situation is slowly improving, but too slow and problems *never* get completely eliminated. /rant Well, I'm out of here.
Mar 25 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
grauzone:
 For your information, programming in Delphi (modern Pascal dialect) was 
 quite a joy.

I agree, D and C# are better, but Delphi and FreePascal are not bad languages to use.
 You must 
 have had only experience with early Pascal dialects... and then never 
 looked at anything that smelled like Pascal...

From the first Pascals and the currently available FreePascal there's a night-day difference. It has OOP, templates, low level constructs, a standard library, good strings, etc. And your binaries are tiny. You can't judge FreePascal from the ancient Pascals. Bye, bearophile
Mar 25 2010
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
grauzone wrote:
 Walter Bright wrote:
 That's funny that you're saying this. Casts are totally messed up in D. 
 Some casts do safe operations (casts between objects and interfaces), 
 some are absolutely ridiculous and only useful in low level situations 
 (casting array slices), some are safe whenever the compiler feels like 
 it (array casts of array literals versus array slices), and some 
 fundamentally break other language features, even accidentally (immutable).

The unsafe casts are not allowed in safe mode.
 casts are easily grepable, but you never know what a specific cast 
 actually does. Think about what this means for generic templated code.
 
 In summary, I'd say casting rules in D are the worst spawn of hell.
 
 I mean, that's ok, it doesn't exactly make D useless. And you can always 
 introduce your own safe (exe bloating, sigh) casting template functions. 
 But I still find it funny that you say this.

Sure, but I think it misses the point a bit. My point was that one should strive to eliminate casts from your code. A type system that requires a lot of casts is going to be less safe than one that does not.
 Implicit integral conversions are not without problems, but when I 
 found C I threw Pascal under the nearest bus and never wrote a line in 
 it again. The taste was so bad, I refused to even look at Modula II 
 and its failed successors.

For your information, programming in Delphi (modern Pascal dialect) was quite a joy. It combined the advantages of the low level programming of C (pointers, inline assembler), was safer than C, and included a sane object model similar to Java/C#. Sounds familiar?

If there's a particular aspect of Delphi you feel would be a good fit for D, please make a proposal. There's no surprise you like both C# and Delphi, as they were created by the same person!
 (Yeah, the template fetishists must have been very unhappy with it.)
 
 I really don't understand why you're bashing Pascal at large. You must 
 have had only experience with early Pascal dialects... and then never 
 looked at anything that smelled like Pascal... and this as a language 
 designer??

There are thousands of languages out there. If I did due diligence researching them all, I'd never finish, as new languages get created faster than anyone could ever study them. At some point, you've gotta pick and choose what you're going to look at in depth. Each language family is based on a core set of principles that get carried from one version to the next. Pascal's core set is unbearably restrictive to me. Sure, a lot of people strongly disagree, and that's fair, it is subjective, after all. Furthermore, like I said, anyone can propose features from any language they feel would make a good fit for D. None will be automatically rejected just because it came from a Pascal family language.
 PS: while you guys are talking about new "absolutely necessary" language 
 features, people new to D are despairing to get a working D installation 
 and *trying* to use external libraries from dsource (that kind of 
 struggling really isn't nice to watch), and people "old" to D are 
 desparing over compiler regressions and random bugs that have global 
 effects on middle-sized to large codebases (circular dependency and 
 optlink bugs come to mind). The situation is slowly improving, but too 
 slow and problems *never* get completely eliminated.

I understand the issues, but we have to get the feature set for D2 finalized before it can be made stable. D1 is stable. The optlink problems have been solved months ago. The compiler regressions have been unanticipated effects from fixing long standing problems in bugzilla, and fixing them has been taken care of reasonably quickly. I'd welcome your help in fixing issues you feel are most important. There are a lot of people working hard on them, but we can use more help. After all, there is no corporation pouring billions into D. We're a bunch of volunteers.
Mar 25 2010
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Walter Bright:
 There are thousands of languages out there. If I did due diligence researching 
 them all, I'd never finish, as new languages get created faster than anyone 
 could ever study them. At some point, you've gotta pick and choose what you're 
 going to look at in depth.

I agree. On the other hand using few days every year, to try in their native environment one or two new languages every year is good, to keep a hold of what others are doing. In the past I have suggested C#3 and Scala. So you can learn why people ask for IDE-related features, or a useful unit-testing, etc.
 There are a 
 lot of people working hard on them, but we can use more help. After all, there 
 is no corporation pouring billions into D. We're a bunch of volunteers.

I think that eventually I will try to fix D bugs myself :-) Regarding base type names I have proposed : byte => sbyte wchar => char16 (or shortchar) dchar => char32 (or intchar) http://d.puremagic.com/issues/show_bug.cgi?id=3850 http://d.puremagic.com/issues/show_bug.cgi?id=3936 Bye, bearophile
Mar 25 2010
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
bearophile wrote:
 Regarding base type names I have proposed :
 byte => sbyte
 wchar => char16 (or shortchar)
 dchar => char32 (or intchar)

Yes, we can endlessly rename keywords, but in the end, what does that accomplish that would compensate for upending every D program in existence?
Mar 25 2010
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Walter Bright:

Yes, we can endlessly rename keywords, but in the end, what does that
accomplish that would compensate for upending every D program in existence?<

I can list few pro/cons, but then the decision is not mine. I'll close my "bug" report on the base of the answers, because this is only one of the about 15 little breaking changes I have proposed (the disallowing of the octals syntax was another one of them, but after your last answer I consider it a closed problem, the http://d.puremagic.com/issues/show_bug.cgi?id=2656 can be closed). The type names in D have a nice symmetry, and they are not ex-novo, they are diffused in other languages. I have appreciated them. I think of a "byte" as unsigned value. This has produced a small bug in a D program of mine. I think changing the names of the signed/unsigned values can solve this. In C# the type names have the same symmetry as in D, with u for the unsigned ones, but later I have found that it seems C# devs agree with me in thinking of bytes as unsigned, because they break the symmetry using: The signed/unsigned bytes in C# are: - The sbyte type represents signed 8-bit integers with values between -128 and 127. - The byte type represents unsigned 8-bit integers with values between 0 and 255. C# is usually a carefully designed language, much better designed than C++, so its example is not negligible. Yet, I don't fully like the C# solution, because even if I think of bytes as signed, all the other C#/D types that don't start with "u" are signed (well, chars too are an exception). So C#/D newbies can follow the symmetry and write wrong code again. So I have suggested ot keep the "ubyte", deprecate the "byte", and introduce a "sbyte" (signed byte). Now it's easy to tell apart what's the signed and what's the unsigned. "byte" can later be removed. ----------------- The wchar/dchar are short names, easy to write, but for me and a person I've shown/taught D it doesn't result easy to remember their size in bytes. "w" stands for wide, "d" for double, this is easy to remember. But how wide is wide? That's why I have suggested to adopt more descriptive names for them. A way to invent descriptive names is to use names similar to the byte/shot/int/long integers. Or to use numbers after the "char". I guess now it can be too much late to change type names... Bye, bearophile
Mar 25 2010
next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"bearophile" <bearophileHUGS lycos.com> wrote in message 
news:hoh07b$16km$1 digitalmars.com...
 Walter Bright:

Yes, we can endlessly rename keywords, but in the end, what does that 
accomplish that would compensate for upending every D program in 
existence?<

I can list few pro/cons, but then the decision is not mine. The wchar/dchar are short names, easy to write, but for me and a person I've shown/taught D it doesn't result easy to remember their size in bytes. "w" stands for wide, "d" for double, this is easy to remember. But how wide is wide? That's why I have suggested to adopt more descriptive names for them. A way to invent descriptive names is to use names similar to the byte/shot/int/long integers. Or to use numbers after the "char". I guess now it can be too much late to change type names...

As long as we're bikeshedding on type names, I do find it misleading that "char" represents a code-unit while still calling itself a "character". Don't get me wrong, I don't mind that at the language level D operates on code-units instead of code-points (Tango and Phobos2 have pretty darned good handling of code-points anyway). It's just that ever since learning how Unicode works, it seems rather a misleading misnomer to call a code-unit "char". I can live with it, of course, now that I know, but I don't envy the newbies who may come across it.
Mar 25 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Nick Sabalausky:
 As long as we're bikeshedding on type names,

"There are only two hard things in Computer Science: cache invalidation and naming things" ^_^
I do find it misleading that "char" represents a code-unit while still calling
itself a "character".<

The development of the BitC language has restarted, it's a system language that looks like Scheme. It's efficient. This is is the start of a small thread about unicode in BitC: http://www.coyotos.org/pipermail/bitc-dev/2010-March/001812.html http://www.coyotos.org/pipermail/bitc-dev/2010-March/thread.html#1812 They agree with me that 32 bit chars can be not really true chars, so I think essentially even UFT32 is an bidirectional Range. Bye, bearophile
Mar 25 2010
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
bearophile wrote:
 The wchar/dchar are short names, easy to write, but for me and a person I've
 shown/taught D it doesn't result easy to remember their size in bytes. "w"
 stands for wide, "d" for double, this is easy to remember. But how wide is
 wide? That's why I have suggested to adopt more descriptive names for them.

The wchar and dchar stem from the popular WORD and DWORD sizes on the x86 platforms. wchar_t is of course "wide character" for C, and is often used for UTF-16 at least on Windows platforms. Confusingly, wchar_t on Linux is 32 bits.
Mar 25 2010
next sibling parent "Nick Sabalausky" <a a.a> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:hoh85i$1kbj$1 digitalmars.com...
 bearophile wrote:
 The wchar/dchar are short names, easy to write, but for me and a person 
 I've
 shown/taught D it doesn't result easy to remember their size in bytes. 
 "w"
 stands for wide, "d" for double, this is easy to remember. But how wide 
 is
 wide? That's why I have suggested to adopt more descriptive names for 
 them.

The wchar and dchar stem from the popular WORD and DWORD sizes on the x86 platforms. wchar_t is of course "wide character" for C, and is often used for UTF-16 at least on Windows platforms.

I think that's why I never had a problem with wchar/dchar. I've dealt with x86 WORD/DWORD. Though I guess that also indicates why other people may find it unintuitive, not everyone has gone low-level like that.
Mar 25 2010
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Walter Bright:

The wchar and dchar stem from the popular WORD and DWORD sizes on the x86
platforms. wchar_t is of course "wide character" for C, and is often used for
UTF-16 at least on Windows platforms. Confusingly, wchar_t on Linux is 32 bits.<

From such comments of yours, and from the lack of interest from Don, Nick and others, I guess that idea of changing char names is closed, even if I don't like those names. I will update the bugzilla with this. The proposal of renaming the byte => sbyte is not closed/resolved yet. It's a little more important, because the current situation is bug-prone. Bye, bearophile
Mar 26 2010
parent bearophile <bearophileHUGS lycos.com> writes:
I can add something to this long thread.

I don't like C octal literals for aesthetic reasons, because in mathematics
leading zeros before the decimal point are not significant. Programming
languages are not forced to follow math notation conventions, but experience
clearly shows me that when possible it's convenient to follow math notation
because it's widely known, by newbie programmers too, and expert programmers to
know it well, sometimes from primary school, so it's well interiorized. That's
why for example the missing operator precedence rules of Smalltalk are bad. So
I think C octal literals are bad-looking small traps that a modern language is
better without. If octal literals are seen as useful, then a better, more
explicit and safer octal literal can be invented. This is what Python3 has
done, and this is what I think D should do. This is what I have asked in bug
report 3837.

On the other hand no octal number has ever caused a bug in my Python2.x or D
programs, and I think it has caused no bugs even in my C code. So despite being
little traps for me they are not so bug-prone.

On the other hand, the name of signed bytes has caused one or more hard-to-find
bugs in my D code. I have never put such bug in Delphi/C programs (but in C the
signedness of chars has caused me few troubles in the past. Thanks Walter D
chars don't come in signed and unsigned versions, avoiding that chars bugs, and
introduces bugs on bytes...). It's not just a matter of bug count: D forces me
to keep some of my attention on the signedness of bytes when I program, this
slows down programming a bit and distracts a small part of my attention away
from the things that truly matter, that is problem solving, the things that the
program has to do, etc. So I think on this D is worse than Delphi/C, and
deserves to be fixed.

My D1 dlibs are something like 80-90 thousand lines of code, and then there is
all the code that uses those dlibs, so I have probably written 250_000 or more
lines of D1 code, this can be more than the D1 code written by Walter. So I am
able to see what things in D1 have caused me bugs or require some undeserved
attention to write bug-free code.

Bye,
bearophile
Mar 27 2010
prev sibling parent Don <nospam nospam.com> writes:
Walter Bright wrote:
 bearophile wrote:
 Regarding base type names I have proposed :
 byte => sbyte
 wchar => char16 (or shortchar)
 dchar => char32 (or intchar)

Yes, we can endlessly rename keywords, but in the end, what does that accomplish that would compensate for upending every D program in existence?

Removing a frequent bug. IMHO, wchar and dchar are fine as is. But byte --> sbyte I support. 'byte' is a really, really awful name. EVERYONE makes the mistake of thinking 'byte' is unsigned. I still do it, really frequently. I believe that almost every existing use of 'byte' is a bug! BTW I don't think that "everyone" is much of an exaggeration. For example, YOU have done it! (The first version of the htod utility used 'byte' where it should have been 'ubyte'). And if even you find it unintuitive, I think the entire planet finds it unintuitive.
Mar 25 2010
prev sibling parent reply "Aelxx" <aelxx yandex.ru> writes:
"bearophile" <bearophileHUGS lycos.com>wrote :
 Regarding base type names I have proposed :
 byte => sbyte
 wchar => char16 (or shortchar)
 dchar => char32 (or intchar)

 http://d.puremagic.com/issues/show_bug.cgi?id=3850
 http://d.puremagic.com/issues/show_bug.cgi?id=3936

 Bye,
 bearophile

In my embedded C projects I always use u8, i8 u16, i16 u32, i32 f32 for floats and they are all defined in user's header file, surely IDE highlights them as keywords :), so no need for language changes =).
Mar 26 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Aelxx:
 In my embedded C projects I always use
 u8, i8
 u16, i16
 u32, i32
 f32 for floats
 and they are all defined in user's header file, surely IDE highlights them 
 as keywords :), so no need for language changes =).

D tries to give sensible defaults, good for most programmers, also to increase code uniformity across different codebases. Python shows that having common style and idioms is very useful to build better a community of shared modules. Bye, bearophile
Mar 26 2010
prev sibling parent reply Ellery Newcomer <ellery-newcomer utulsa.edu> writes:
On 03/25/2010 01:08 PM, Walter Bright wrote:
 There are thousands of languages out there. If I did due diligence
 researching them all, I'd never finish, as new languages get created
 faster than anyone could ever study them. At some point, you've gotta
 pick and choose what you're going to look at in depth. Each language
 family is based on a core set of principles that get carried from one
 version to the next. Pascal's core set is unbearably restrictive to me.
 Sure, a lot of people strongly disagree, and that's fair, it is
 subjective, after all.

 Furthermore, like I said, anyone can propose features from any language
 they feel would make a good fit for D. None will be automatically
 rejected just because it came from a Pascal family language.

What do you think of Erlang's bit syntax if you've looked at it, or could you if you haven't? Beyond the syntax, which is totally incompatible with D, I think it's pretty nifty and would be a sweet thing to have as a library template at the least.
Mar 25 2010
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Ellery Newcomer wrote:
 What do you think of Erlang's bit syntax if you've looked at it, or 
 could you if you haven't?

I know nothing about it.
Mar 25 2010
parent Ellery Newcomer <ellery-newcomer utulsa.edu> writes:
On 03/25/2010 06:21 PM, Walter Bright wrote:
 Ellery Newcomer wrote:
 What do you think of Erlang's bit syntax if you've looked at it, or
 could you if you haven't?

I know nothing about it.

I suppose you could think of it as sort of a regex-for-generic-data, but you can use it as a pattern matcher or a data constructor. The example Armstrong gives in Programming Erlang is <<2#11111111111:11,B:2,C:2,_D:1,E:4,F:2,G:1,Bits:9>> = X where X is assumedly a byte array or something. the first 11 bits are asserted to be 1, and the remaining bits get associated with the following variables. Yeah, that example isn't much more impressive than bitfields. You could do something like <<N:32, Data:N/binary, _/binary>> = X which would interpret the first four bytes as a length field, bind the next N bytes to Data, and ignore the rest.. The general syntax for them should look something like Bit: << >> << E >> E: E , E1 E1 E1: Value Value : Size Value / TypeSpecList Value : Size / TypeSpecList //integral, fp, or binary (byte arrays, strings, etc) type Value: Expression // integral type Size: Expression TypeSpecList: TypeSpecList - TypeSpec TypeSpec TypeSpec: Endianness Sign Type Unit // default is big Endianness: big little native Sign: signed unsigned Type: integer float binary //Size*Unit is the number of bits that get matched // Unit defaults to 1 for Type = integer or float, 8 for Type = binary Unit: IntegerLiteral
Mar 25 2010
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Walter Bright:
Disabling implicit integral conversions would make it unbearable to use.<

Implicit conversion from signed to unsigned is too much unsafe (if you don't have integer overflows). C# can teach us two lessons on this. C# has disabled some implicit conversions and keeps other of them. Bye, bearophile
Mar 25 2010
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/25/2010 06:58 AM, bearophile wrote:
 Walter Bright:
 Disabling implicit integral conversions would make it unbearable to use.<

Implicit conversion from signed to unsigned is too much unsafe (if you don't have integer overflows). C# can teach us two lessons on this. C# has disabled some implicit conversions and keeps other of them.

The problem is it asks you to insert casts all too often. I think the value range propagation is a principled and correct solution to that problem. Andrei
Mar 25 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Andrei Alexandrescu:
 The problem is it asks you to insert casts all too often. I think the 
 value range propagation is a principled and correct solution to that 
 problem.

In bugzilla I have asked to disable the implicit conversion enum => base type in an equality test, see: http://d.puremagic.com/issues/show_bug.cgi?id=3999 In bugzilla I have never asked to disable implicit signed => unsigned casts, I am able to see they are two quite different situations. I think the implicit conversions from enum => base types are uncommon enough, so they can't introduce too many casts. If Walter doesn't agree with me (and few other people here) then there's no point in keeping bug 3999 open, it can be closed. Because it's better to fix/change similar things now, when the D2 language is not diffused yet. Later such changes are harder to do. In bugzilla there are few other things that deserve a similar look. Those changes can't be done all at the same time, but the decision can be taken in a short enough time, and it's better to think about them now. Bye and thank you for your attention, bearophile
Mar 25 2010
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/25/2010 11:17 AM, bearophile wrote:
 Andrei Alexandrescu:
 The problem is it asks you to insert casts all too often. I think the
 value range propagation is a principled and correct solution to that
 problem.

In bugzilla I have asked to disable the implicit conversion enum => base type in an equality test, see: http://d.puremagic.com/issues/show_bug.cgi?id=3999 In bugzilla I have never asked to disable implicit signed => unsigned casts, I am able to see they are two quite different situations. I think the implicit conversions from enum => base types are uncommon enough, so they can't introduce too many casts. If Walter doesn't agree with me (and few other people here) then there's no point in keeping bug 3999 open, it can be closed. Because it's better to fix/change similar things now, when the D2 language is not diffused yet. Later such changes are harder to do. In bugzilla there are few other things that deserve a similar look. Those changes can't be done all at the same time, but the decision can be taken in a short enough time, and it's better to think about them now. Bye and thank you for your attention, bearophile

I think defining a integral() function that gives the value of the enum as an appropriately-typed integral number wouldn't harm. Andrei
Mar 25 2010
parent reply "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
Andrei Alexandrescu wrote:
 On 03/25/2010 11:17 AM, bearophile wrote:
 Andrei Alexandrescu:
 The problem is it asks you to insert casts all too often. I think the
 value range propagation is a principled and correct solution to that
 problem.

In bugzilla I have asked to disable the implicit conversion enum => base type in an equality test, see: http://d.puremagic.com/issues/show_bug.cgi?id=3999 In bugzilla I have never asked to disable implicit signed => unsigned casts, I am able to see they are two quite different situations. I think the implicit conversions from enum => base types are uncommon enough, so they can't introduce too many casts. If Walter doesn't agree with me (and few other people here) then there's no point in keeping bug 3999 open, it can be closed. Because it's better to fix/change similar things now, when the D2 language is not diffused yet. Later such changes are harder to do. In bugzilla there are few other things that deserve a similar look. Those changes can't be done all at the same time, but the decision can be taken in a short enough time, and it's better to think about them now. Bye and thank you for your attention, bearophile

I think defining a integral() function that gives the value of the enum as an appropriately-typed integral number wouldn't harm. Andrei

Why not let to!int() do this? -Lars
Mar 25 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Lars T. Kyllingstad:
 Why not let to!int() do this?

Because if the enum is for example: enum ulong Foo { ... You are casting an ulong to int and you risk losing bits of information. That's why Andrei has suggested a conversion template function, because it can (I don't know how) find the correct base type and cast the enum to it. Andrei is sometimes one step forward. Bye, bearophile
Mar 25 2010
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/25/2010 11:57 AM, Lars T. Kyllingstad wrote:
 Andrei Alexandrescu wrote:
 On 03/25/2010 11:17 AM, bearophile wrote:
 Andrei Alexandrescu:
 The problem is it asks you to insert casts all too often. I think the
 value range propagation is a principled and correct solution to that
 problem.

In bugzilla I have asked to disable the implicit conversion enum => base type in an equality test, see: http://d.puremagic.com/issues/show_bug.cgi?id=3999 In bugzilla I have never asked to disable implicit signed => unsigned casts, I am able to see they are two quite different situations. I think the implicit conversions from enum => base types are uncommon enough, so they can't introduce too many casts. If Walter doesn't agree with me (and few other people here) then there's no point in keeping bug 3999 open, it can be closed. Because it's better to fix/change similar things now, when the D2 language is not diffused yet. Later such changes are harder to do. In bugzilla there are few other things that deserve a similar look. Those changes can't be done all at the same time, but the decision can be taken in a short enough time, and it's better to think about them now. Bye and thank you for your attention, bearophile

I think defining a integral() function that gives the value of the enum as an appropriately-typed integral number wouldn't harm. Andrei

Why not let to!int() do this? -Lars

Because an enum that uses long as a base will not be converted. integral() would automatically deduce and return the appropriate base type. Andrei
Mar 25 2010
parent "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
Andrei Alexandrescu wrote:
 On 03/25/2010 11:57 AM, Lars T. Kyllingstad wrote:
 Andrei Alexandrescu wrote:
 On 03/25/2010 11:17 AM, bearophile wrote:
 Andrei Alexandrescu:
 The problem is it asks you to insert casts all too often. I think the
 value range propagation is a principled and correct solution to that
 problem.

In bugzilla I have asked to disable the implicit conversion enum => base type in an equality test, see: http://d.puremagic.com/issues/show_bug.cgi?id=3999 In bugzilla I have never asked to disable implicit signed => unsigned casts, I am able to see they are two quite different situations. I think the implicit conversions from enum => base types are uncommon enough, so they can't introduce too many casts. If Walter doesn't agree with me (and few other people here) then there's no point in keeping bug 3999 open, it can be closed. Because it's better to fix/change similar things now, when the D2 language is not diffused yet. Later such changes are harder to do. In bugzilla there are few other things that deserve a similar look. Those changes can't be done all at the same time, but the decision can be taken in a short enough time, and it's better to think about them now. Bye and thank you for your attention, bearophile

I think defining a integral() function that gives the value of the enum as an appropriately-typed integral number wouldn't harm. Andrei

Why not let to!int() do this? -Lars

Because an enum that uses long as a base will not be converted. integral() would automatically deduce and return the appropriate base type.

Ah, now I understand. I thought you meant a function to convert an enum to a user-specified type, i.e. integral!T(myEnum). -Lars
Mar 25 2010
prev sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
KennyTM~ <kennytm gmail.com> wrote:

 On Mar 26, 10 05:46, yigal chripun wrote:
 while it's true that '?' has one unicode value for it, it's not true  
 for all sorts of diacritics and combine code-points. So your approach  
 is to pass the responsibility for that to the end user which in  
 99.9999% will not handle this correctlly.

Non-issue. Since when can a character literal store > 1 code-point?

Not only that, but one does not routinely go about adding random numbers to randomly chosen code points. When '?' + 3 is used, it's because it was the best (fastest/easiest/most readable) way to do it. Or somebody was just showing off, but that can be done in more horrible ways. -- Simen
Mar 27 2010