

How Enums Spread Disease – And How to Cure It (2012) - gpvos
http://codecraft.co/2012/10/29/how-enums-spread-disease-and-how-to-cure-it/

======
gkanapathy
Seems to me that the whole point of OOP is programming is to encapsulate this
information. An enum is basically just a degenerate class. If you're finding
that you are doing a case on your enum all over the place, you should probably
be defining them as objects (or possibly subclasses).

The solution of creating a bunch of tuples (or structs)...well, that's just
creating a bunch of objects. The language and paradigm already has a way of
dealing with these that's a bit more flexible and powerful than tuples.

~~~
altcognito
> The solution of creating a bunch of tuples (or structs)...well, that's just
> creating a bunch of objects.

Essentially in Java that's actually what you're doing, and if you override the
constructor of the enum you get a nice version of what the author is
describing (but with more flexibility than a straight up Map)

(oh, bthornbury covered this in his comment)

------
stinos
I was nodding in agreement throughout the article until the solution was
presented, or at least the implementation of it. Maybe it's truly genius and
am I just not understanding it properly, but the fact I had to go over it
multiple times to grasp wtf is going one isn't exactly a good sign, I think.

The idea behind the solution is definitely ok: you define a type with a bunch
of immutable properties describing it. But surely there must be more
elegant/easier to read/easier to understand ways to do this than to have a
bunch of macros/includes inside enums and whatnot? Moreover the TUPLE macro is
declared twice with the same arguments (and then the arguments are repeated
once more) so that means extending the type has to be done in 2 places which
is exactly one of the things warned about earlier.

~~~
Someone1234
This is exactly why I am really happy to be working in a language which
doesn't support macros. Are they performant? Yes. But they absolutely ruin
code readability and make debugging harder also (as you're stepping through
"code" which only exists post-compile).

I felt like the OP made a great point about enums and I agree.

~~~
EpicEng
They do have downsides, but you gloss over the main reason to use macros;
they're an extremely powerful tool.

~~~
Someone1234
Meh. People are expensive, compute is cheap. If code is twice as hard to
debug, you better have a darn cost saving reason to be working in inefficient
language like C/C++.

~~~
jeorgun
Macros really aren't about performance, though (or at least they shouldn't be;
inlining and/or constexpr are a better solution to that problem in basically
every respect). They're either about simple, potentially extremely unsafe,
syntax extensions (as in the article) or about using metadata that wouldn't
otherwise be available (e.g. filenames/line numbers in the assert macro).

Not that I think macros are a good thing– 99% of the time they're best
avoided– but dismissing them because 'performance isn't important' is
basically a non sequitur.

~~~
copsarebastards
Plenty of languages provide filenames/line numbers in assertions without using
macros.

Furthermore, `__LINE__` and `__FILE__` style macros are a pretty safe subset
of macros that aren't really representative of the problems with macros.
Compare:

    
    
        #include <stdlib.h>
        #include <stdio.h>
        #include "my_macro_library.h"
    
        int main()
        {
            int x = 0;
            printf("%i", increment(x));
    
            printf("%i", __LINE__);
    
            return EXIT_SUCCESS;
        }
        
    

There are a ton of different implementations of an `increment` macro which are
going to cause undefined behavior here. Contrast with the `__LINE__` macro:
it's implemented by the compiler and has unsurprising, well-defined behavior.

------
powertower
So in other words, only use Enums to declare a set of 1-dimensional items that
can't be broken down further (i.e., item is a property, not an object).
Otherwise, you are going to run into trouble.

Isn't that kind of the definition of an Enum vs. Struct vs. Class/Object? And
the problem lies with what enums are being used for in some cases, rather than
enums being a bad practice?

~~~
masklinn
Depends on the language. Java enums can hold arbitrary attributes (the "cure"
is built-in), and algebraic data types go significantly further as variants
can have completely different fields.

------
bthornbury
This is a good look at the dangers of diffusing semantic knowledge in general.
His approach is much more true to OOP paradigms.

In Java, you can take this a stop further, and use enums basically as
enumerated instances of a class. Example:

public enum Stuff { Stuff1(param1), Stuff2(param2);

    
    
        private Object param;
        private Stuff(Object param){
            this.param = param;
        }
    
        public getParam(){
            return this.param;
        }

}

Now you can call Stuff1.getParam();

~~~
masklinn
> In Java, you can take this a stop further, and use enums basically as
> enumerated instances of a class.

Isn't it half the point of java's enums? (the other half being type-safe
enums).

------
jpatte
The solution presented here basically consists of implementing a VehicleType
class with a bunch of static instances without using the class syntax. I can't
decide if this was a deliberate decision from the author or if he just didn't
realize this.

~~~
JoeAltmaier
Maybe to be C-compatible?

------
Enthouan
I guess the way to solve this disease is just to use Enum for what they are
supposed to be used for. Which is storing a state, that's it.

This article really lacks of OOP concept. In the first example the enum is a
property on the 'vehicule' object, it would be way better to add all the
properties needed to that class instead of creating a weird 'VehicleTypeTuple'
struct...

------
yuriks
In my experience, most of the evils of enums can be traced back to the lack of
exhaustive pattern matching in C++/Java. When programming in Rust or Haskell,
enums are used much more often, and the exhaustiveness enforcement by the
compiler means that these kinds of use cases tend to be encapsulated inside
helper functions, which end up being equivalent to the members of the "tuples"
presented by the author. Any additions or changes to the enum will
automatically make the compiler force you to update these functions.

------
karmakaze
The title is a bit misleading--maybe 'plain enums'. In Java for instance, all
the _solutions_ can all be encapsulated within the language's 'enum' type.

~~~
geonik
I second that; Java has solutions for all problems described by this post.

\- constructors, private fields, methods, addressing the "separations of
concerns" \- how about Enum.valueOf(enumClass, text.toUpperCase()) to avoid
the "classic shadow array of string literals"?

Here is an example of what can be achieved by Java enums

    
    
      enum Type {
        ANIMAL(null),
        MAMMAL(ANIMAL),
        DOG(MAMMAL) {
          public void makeNoise() {
            System.out.println("Woof");
          }
        },
        CAT(MAMMAL) {
          public void makeNoise() {
            System.out.println("Meow");
          }
        };
        private Type parent;
        Type(Type parent) {
          this.parent = parent;
        }
        public boolean isA(Type type) {
          if(this == type) return true;
          if(parent != null) return parent.isA(type);
          return false;
        }
        public void makeNoise() { }
      }
      assert Type.DOG.isA(Type.MAMMAL) == true;
      assert Type.CAT.isA(Type.DOG) == false;
      Type.CAT.makeNoise(); // "Meow"

~~~
ajanuary
This demonstrates a different problem - enums are closed. If I want to add an
Owl, I can't do it without editing Type. This might break the API contract (if
you own the library) or not be possible (if it's a third part library).

There is very little reason to use an enum over classes in this example. It's
rare that I've seen a Java class style enum that wouldn't have been better
served by classes (including some that I've written myself and regretted!)

~~~
jdmichal
For this example, I agree that it makes very little sense. The question you
really need to ask yourself whenever you are making an `enum` is: Does it make
sense that this type is restricted to a predefined selection of instances?
There are times when the legitimate answer to this question is "yes", and in
those cases go ahead and make an `enum` and enjoy its benefits.

~~~
devonkim
Historically, I've used rich enums in Java to be able to use switch statements
to reason about a system state and as poor man's type pattern matching.
Sometimes the switch is handy for making a state machine clearer, and
sometimes it makes it easier to explain the relationships of these states as
an ordered list. Another important use of enums is if you're trying to create
a rich set of annotations that take parameters, which would force you into
dropping a lot of the Java OOP typing available. I can't have a closure or
even an anonymous class as an argument to parameterized annotations - and this
is definitely by design.

I got bit pretty hard before trying to design an annotation-driven convention
for a library I was writing (I had been writing a lot of Python for a while)
and realized I'd have to squash a bunch of my carefully arranged classes into
an enum. You run into similar problems when marshalling your objects to, say,
a Thrift structure definition.

------
jblow
This does not read to me like good advice from an experienced programmer.

------
kerkeslager
It has been a while since I wrote Java, but if my memory is correct, Java has
a much cleaner solution to this. In Java an enum is just a class with a fixed
number of instances, each with a constant name. You can make the instances
private and expose all of your behavior through the interface of the class,
using a "factory" method to find the correct instance (I say factory in quotes
because it doesn't create the instance, it just finds the correct instance,
which already exists). There's nothing stopping you from doing a switch-case
against the instances, but at least if someone does that it will be limited to
the enum class. And with some discipline you can define all the properties in
the enum's constructor so no switch-cases are necessary. This mitigates all
the OP's concerns, I think.

In C# you can use extension methods to keep all your switch-cases in one
place, but it takes a little more work to enforce this with private
visibility. I haven't thought through this completely, but my first attempt
might be to replace the enum with a class with a private constructor and
expose public static const instances, or maybe even hide the instances behind
a factory method. I haven't tried this in practice, however, so I'm not sure
what the implications of that approach are.

------
qznc
Why not simple subclasses? E.g. class Truck extends Vehicle. From the article,
the argument would be that "everything in one file" is desired, so you can
e.g. parse it from other or for other languages.

There are also the downsides of the table approach. What does avg_km_per_liter
mean for e-cars? With multiple classes they can have different fields.

Everything is a tradeoff.

------
arielby
This _is_ the standard style of programming in functional programming
languages. While it isn't object-oriented (being its dual), I don't see why is
it so bad (I wouldn't like a single `Vehicle` class to have methods handling
lane use in each state).

------
spacemanmatt
One of the antipatterns I see in db schemas is over-use of just one field to
indicate status or action, if not both. It's the same basic deal of
overloading enums as the article demonstrated -- every instance increases your
pain of complexity.

So, I guess, that's to say, this is a little bigger than just enums.

------
CountSessine
Often called an X-macro

[http://en.m.wikipedia.org/wiki/X_Macro](http://en.m.wikipedia.org/wiki/X_Macro)

------
anon4
Shouldn't this all be in a config file or vehicle database in the first place
and not hardcoded?

