digitalmars.D - Strongly typed enums
- bearophile (52/52) Aug 25 2011 I have discussed a bit this topic probably one year ago, but it's good t...
- Adam Ruppe (7/7) Aug 25 2011 I think I'm for this. If you provide a name with no base type,
- bearophile (7/12) Aug 25 2011 I think I am not able to do that yet.
- Adam D. Ruppe (14/18) Aug 25 2011 With a base type, it looks like inheritance, so I expect some
I have discussed a bit this topic probably one year ago, but it's good to talk some more about it. Jane Street is a known trading firm, they use the OCaML language because it's efficient and because its type safety, it is a very statically typed language. For them one of the simple but important qualities of OcaML (and some other functional language) are the "match" statement (for pattern matching) because it statically forbids missed cases and redundant cases. This is very useful because if you add one more case in an enumerated type, the compiler will produce an error for all the match present in your whole program. This makes much more safe to modify code. They talk about it here, from 33 minutes, it's an interesting video on the whole: http://vimeo.com/14317442 I presume Walter has added the "final switch" to D for the same purpose: if you add or remove one element from an enum, final switches will give you errors at compile time. This removes a common source of bugs in C code. Recently I have created a bug caused (not caught) by the nature of D enums. This is just the last one of similar bugs. The following is reduced code of a small game. The main contains a while that loops until the game is finished. The original version of this program was simpler, and instead of using the GameState enum, it just used 0, 1 and -1 constants in the code. So the original version of isFinished tests if winner() != -1. I have added the enum GameState, now winner() returns. But I have forgotten to update the isFinished() function too. The D language doesn't catch that simple bug: enum GameState { inProgress, draw, humanWins, computerWins } GameState winner() { // this function used to return -1, 1, 0 values // ... } bool isFinished() { return winner() != -1; // not updated function! //return winner() != GameState.inProgress; // correct code! } void main() { while (!isFinished()) { // ... } // ... } In a bigger program it becomes less easy to catch a similar bug (this bug was not found also because of another waeak typing characteristic of D language: inside isFinished it allowes you to compare an unsigned size_t value with -1, despite -1 is statically visibly outside the range of possible unsigned values). If I write similar code in C++11, it catches that bug: enum class GameState { inProgress, draw, humanWins, computerWins }; GameState winner() { return GameState::draw; } bool isFinished() { return winner() != -1; // line 11, error } int main() {} G++ 4.6.0 outputs: test.cpp: In function 'bool isFinished()': test.cpp:11:25: error: no match for 'operator!=' in 'winner() != -0x000000001' In D final switches where introduced to avoid essentially the same class of bugs. So I think I'd like named D enums to be strongly typed. Weak typing is better left to old versions of the C language. More info in my enhancement request: http://d.puremagic.com/issues/show_bug.cgi?id=3999 Do you know what are the disadvantages of this change in D enums? Do you know if it is going to cause other bugs or problems? If you really want to keep weakly typed enums in D then I am able to invent a new D syntax to specify strongly typed enums, like this, but I suggest to not go this way: typedef enum GameState { inProgress, draw, humanWins, computerWins } Bye, bearophile
Aug 25 2011
I think I'm for this. If you provide a name with no base type, it should work like this. Bearophile, have you considered making a pull request? I doubt it would be too hard in the compiler, and then we can try it to see what code breaks. I imagine if any code breaks, it'd be easy enough to fix with changing to the item or inserting a cast.
Aug 25 2011
Adam Ruppe:I think I'm for this. If you provide a name with no base type, it should work like this.Why do you suggest to disallow implicit conversions (and use strong typing) only if the enum definition doesn't specify a base type?Bearophile, have you considered making a pull request?I think I am not able to do that yet.I imagine if any code breaks, it'd be easy enough to fix with changing to the item or inserting a cast.Recently I have discussed safe enum conversions too. In some cases it's good to replace cast() with a to!. http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=142862 Bye, bearophile
Aug 25 2011
bearophile wrote:Why do you suggest to disallow implicit conversions (and use strong typing) only if the enum definition doesn't specify a base type?With a base type, it looks like inheritance, so I expect some implicit conversions are allowed. class A : Base {} Base a = new A(); // allowed enum ENUM : int {} int a = ENUM.something; // same deal? i think soI think I am not able to do that yet.You should try - the compiler isn't too hard, and github isn't bad once you've done it once. So then you'll be able to try these proposed features and it's easier to get a patch accepted than to hope Walter does it all.In some cases it's good to replace cast() with a to!.I use to a lot with enums. It's really convenient how it goes to and from string based on the member name. I don't think I've ever actually tried to! with int!
Aug 25 2011