Strongly typed enums

bearophile bearophileHUGS at lycos.com
Thu Aug 25 03:23:01 PDT 2011


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


More information about the Digitalmars-d mailing list