[HN Gopher] When are enums not a code smell?
___________________________________________________________________
When are enums not a code smell?
Author : danny00
Score : 26 points
Date : 2021-11-21 10:57 UTC (1 days ago)
(HTM) web link (softwareengineering.stackexchange.com)
(TXT) w3m dump (softwareengineering.stackexchange.com)
| [deleted]
| bob1029 wrote:
| For me, enums are just a way to eliminate canonical magic strings
| throughout. They also have the happy side effect of storing as an
| integer in the database, but I do sometimes apply a .ToString()
| in those situations where users will be looking at the data
| frequently.
|
| There are actually very few cases where I would say enums should
| not be used. The most obvious one being open-ended types where
| the list of members will always be growing (e.g. if you made each
| user of a system a member). You should also not use enums to
| convey a compounding of logical facts - these get nasty really
| fast in my experience. Sometimes it is better to just have 5-10
| boolean flags on a type than to try and cram every logical
| combination of those things into canonical enumeration members.
| gjulianm wrote:
| You can use flag enums for the logical facts. Not always, but
| sometimes they can be useful and make comparisons/checks
| easier.
| echelon wrote:
| I'm going to flag this submission. This is horrible Stack
| Overflow post with bad answers.
|
| Languages have different types of "enums". Enums in languages
| like Rust are extraordinarily powerful _sum types_ that create
| compact memory footprints, strong compile time guarantees, and
| can form the basis of expressive and correct state machines.
|
| Enums (sum types) are one of the best patterns.
| couchand wrote:
| That's not what flagging is for?
| echelon wrote:
| This is really bad advice.
|
| While the commentary in this HN thread is good, those that
| click the link directly and don't know any better will be
| misinformed.
|
| I seems bad to let this stay on the front page.
| couchand wrote:
| Then downvote. Flagging an article you simply disagree with
| wastes the moderation team's time.
| gary_0 wrote:
| You can't downvote submissions on HN.
|
| From https://news.ycombinator.com/item?id=24123981 user
| 'pwg' says:
|
| > Flagging is the closest to a "dislike" option
| available. But it is suggested to use it for items that
| are off topic.
|
| > The better way to make sure items you find to be
| "positive" make the front page is to upvote everything
| you think deserves to get there. Upvote the positive,
| ignore the negative, and the negative will fade from
| view.
|
| Officially, there's
| https://news.ycombinator.com/newsfaq.html but I wish it
| went into more detail about what the HN norms are
| regarding upvoting/downvoting/flagging.
| danpalmer wrote:
| Modern enums in languages with powerful compilers are one of my
| favourite features and I repeatedly find them to be a marker of
| high quality codebases.
|
| In Python there has historically been a strong tendency to use
| strings as enums, which combined with Python's generally weak
| typing, can lead to all sorts of issues.
|
| I also write a fair bit of Swift at the moment, and in Swift
| codebases I've found that plenty of use of enums, particularly
| those with attached values, leads to easy to use and correct
| code, as well as quite concise code. It's much better than
| codebases that go hard on classes.
| jamil7 wrote:
| Yeah, I use them all the time in Swift to model UI state, I
| find it's a pretty elegant way to avoid invalid states and
| simplifies UI code a lot.
| danpalmer wrote:
| Yeah they're wonderful for that.
|
| Very much like Elm (and/or Haskell). Our whole iOS
| architecture is inspired by the Elm architecture - it's a
| pleasure to work with, and Swift the language makes it feel
| really at home.
| jamil7 wrote:
| We're doing something very similar on macOS and iOS. The
| only rough edge I'm finding with this setup is integrating
| with Core Data feels a bit clunky/verbose.
| amarshall wrote:
| Python enums with static type-checking via Mypy are pretty
| pleasant, though.
| mindvirus wrote:
| I'm in the minority I guess on not liking enums. The main thing
| I've run into repeatedly is difficulty to add to them without
| breaking things. Even if linters catch non-exhaustive switch case
| statements, there's always the if statement that it misses.
| Client compatibility is a big challenge, so I usually end up
| sending them as strings on the wire anyway.
| [deleted]
| exdsq wrote:
| Enums aren't a code smell.
| bo1024 wrote:
| In a functional language like Haskell or O'Caml, it feels like
| half of what you write involves enums (sum types). They're
| fantastic in those contexts and I really miss them in python. But
| OO is a different approach to data and I suspect the "code smell"
| advice comes from not wanting to mix the two paradigms. For
| example
|
| Functional: data Switch = On | Off
| function flip: Switch -> Switch flip s = case s of
| On -> Off Off -> On
|
| Object Oriented: abstract class Switch { Switch
| flip(Switch); } class On : Switch { flip(s) { return new
| Off(); } class Off : Switch { flip(s) { return new On();
| }
|
| Weird mixture: enum State {On, Off};
| class Switch { State myState; Switch
| flip(Switch s) { if (s.myState == Off) { s.myState
| = On; return s ; } if (s.myState == On) {
| s.myState = Off; return s; }
| shikoba wrote:
| Where is File_not_found?
| theknocker wrote:
| If you make heavy use of enums in a class oriented language, your
| code is not as clean as you think it is.
|
| Everyone in this thread sounds like they've been writing c# for
| exactly three years.
| simiones wrote:
| Generally the enum vs polymorphic class debate is actually a
| subset of a better known trade-off between OO style inheritance
| and FP style use of sum types.
|
| The trade-off is between ease of extension in two cases: - adding
| new types to the sum type / new subclasses - adding new methods
| that can operate on all types / subclasses
|
| In a classic OO design, it's easy for any client to add new
| subclasses that implement the same API as the existing classes.
| However, adding a new API means that all subclasses need to be
| changed.
|
| Conversely, in an FP design, adding a new type to the sum type (a
| new enum value) means that all places pattern matching on the sum
| types (switching on the enum) need to be changed. However, adding
| a new operation that works with the existing types is trivial.
|
| To give a more specific example, let's say we want to model an
| expression tree.
|
| An OO design might look like this: abstract class
| BaseExpression { abstract evaluate()
| subexpressions() } class SumExpression extends
| BaseExpression { evaluate() = map(evaluate,
| subexpressions()).Sum() } class NumberExpression
| extends BaseExpression { int x subexpressions() =
| [x] evaluate() = x }
|
| An FP design might look like type ExpressionTree
| a = SumExpresssion [ExpressionTree] | NumberExpression a
| evaluate :: ExpressionTree a -> a evaluate SumExpression xs
| = sum map evaluate xs evaluate NumberExpression x = x
| subexpressions :: ExpressionTree a -> [a] subexpressions
| SumExpression xs = map union xs subexpressions
| NumberExpression x = [x]
|
| Equivalently, an enum based design would have
| enum ExprType { Sum Number }
| struct SumExpression { ExprType *void[]
| Subexpressions } struct NumberExpression {
| ExprType int x } evaluate(e) {
| switch(e->ExprType) { case Sum:
| (SumExpression)e.Subexpressions.map(evaluate).Sum()
| case Number: (NumberExpression)e.x } }
| subexpressions(e) { switch(e->ExprType) {
| case Sum: return (SumExpression)e.subexpressions case
| Number: void*[]{(NumberExpression)e.x} } }
|
| Now, if we want to add support for MultiplyExpression, in the OO
| program we add a new file and we define evaluate() for
| MultiplyExpression. In the FP program we have to modify the
| definitions of ExpressionTree, SumExpression and
| NumberExpression.
|
| Conversely, if we want to add support to print an ExpressionTree,
| in the FP version we just add a new function in a new file and
| describe how to print each type of ExpressionTree. In the OO
| program, we need to modify the implementations of each existing
| BaseExpression subclass to add support for printing.
| Zababa wrote:
| I think it's usually refered as "the expression problem".
| Here's a good post about it:
| https://eli.thegreenplace.net/2016/the-expression-problem-
| an.... The idea is that when you have existing data and
| operations on them, how can you add more data and more
| operations without controlling the original code.
|
| There's also a part about early and late binding. In statically
| typed FP programming, the compiler will warn you where you
| forgot enum cases when you add one. This is because this
| information is known at compile time. If you want to generate
| classes at runtime while keeping the same code, you may prefer
| the late-binding approach of having the class itself handle how
| it reacts to an operation.
| couchand wrote:
| This is called the Expression Problem because, as you allude to
| in your example, it comes up all the time in compilers and
| interpreters when dealing with expressions.
|
| If you're looking for an elegant solution (the first I've ever
| seen), check out the work by Kiselyov on tagless final
| encodings.
|
| The core idea is to invert the interface. Instead of the
| expression types being objects that implement an interface
| consist ing of operations, or the expression types being enum
| values and the operations being total functions of those enums,
| the interface is the abstract logic of the operation, and the
| interface methods are the expression types.
|
| Adding new operations is easy, it's a new interface
| implementation. Adding new expression types is easy, too. You
| create a new interface for the new type, and implement that
| interface for each compatible operation.
|
| It does require typeclasses or traits or something like that,
| and if you want the full power you need language support for
| higher-kinded types. Look at the papers for full examples, but
| here's a teaser in pseudocode: interface
| BoolLogic { fn bool(bool) -> Self; fn
| if(Self, Self, Self) -> Self; } struct Eval {
| value: Anything } implement BoolLogic for Eval
| { fn bool(value) -> Eval { Eval {
| value } } fn if(condition, consequent,
| alternate) -> Eval { if (conditon.value)
| consequent else alternate
| } } struct Print { text: String
| } implement BoolLogic for Print { fn
| bool(value) -> Print { let text =
| value.toString() Print { text } }
| fn if(condition, consequent, alternate) Print {
| let text = "if (" + condition.text +
| ") {" + consequent.text +
| "} else {" + alternate.text +
| "}" Print { text } } }
| interface NumLogic { fn num(int) -> Self
| fn add(Self, Self) -> Self fn eq(Self, Self) ->
| Self } implement NumLogic for Eval {
| fn num(value) -> { Eval { value } }
| fn add(left, right) -> Self { let value =
| left.value + right.value Eval { value }
| } fn eq(left, right) -> Self { let
| value = left.value == right.value Eval { value
| } } } fn munge<T: BoolLogic +
| NumLogic>() -> T { T.if(T.eq(T.add(T.nu.(2),
| T.num(2)), T.num(4)), T.num(42), T.num(0)) }
|
| If you're curious I'd highly recommend reading and implementing
| the papers yourself. Have fun writing a typechecker, it's
| surprisingly easy! (with HKTs...)
| richardwhiuk wrote:
| Design patterns are often heavily weighted towards the languages
| in which they were devised and don't generally apply.
|
| Lots of the classic OOP design patterns are because Java/C++ is
| very rubbish.
| fivelessminutes wrote:
| > "WARNING As a rule of thumb, enums are code smells and should
| be refactored to polymorphic classes. [8]" Seemann, Mark,
| Dependency Injection in .Net, 2011, p. 342
|
| Gosh, what a load of baloney from a frog in a well.
| raverbashing wrote:
| I find it funny how Java (and maybe .NET, though the example is
| .NET - but probably "extended" from Java) seems to have
| catalyzed the worse types of cargo-culting, OO "overdose" and
| similar pseudo-good practices.
|
| Then we end up with "corporate java code" that wants to throw
| out what was the basis of computing (if statements, switches)
| and turn all into some crazy type hierarchy.
| Supermancho wrote:
| Not so much .NET/Java but Structured Programming (1972 Edsger
| W. Dijkstra, Ole-Johan Dahl, and Tony Hoare) has prompted the
| gradual backlash against switches.
| (https://youtu.be/eEBOvqMfPoI?t=1512, re: Duff's Device).
| Senior developers should watch the whole thing to understand
| how block scope and functions and classes relate.
|
| Modern development has drifted toward lots of small functions
| (which causes a mess because modern IDEs dont have supporting
| features like this: https://youtu.be/baxtyeFVn3w?t=1786, re:
| gtoolkit). A switch requires you to look inside and reason
| about the internals of a function to understand how it will
| behave before exiting, as the compiler. This doesn't matter
| so much if you dont care about early exits (no branch
| prediction, etc). Either way, it's extra cognitive overhead
| and error prone for a small improvement in readbaility. It's
| the line where programming becomes tricky programming.
|
| As a thought experiment, imagine there is ANOTHER keyword
| that acts like an if/else (or switch with mandatory breaks).
| If A. it's terse, B. it's structured, C. it's readable, would
| that be better?
|
| We primarily use switch because it's easy to read (versus a
| ton of if{}/else{}), as it gives us almost-block scoping
| without having to define another indirection/function. Python
| has this switch equivalent in match/case, but you have to use
| Python to get it.
|
| Switch use is a syntax problem. Lots of software is colored
| by bad syntax and language design^.
|
| ^ I think most people realize that type hierarchies are a
| brittle/limited form of mixin. Even Java has softened to this
| a bit (aspects, etc) but will likely never be fun to use.
| aardvark179 wrote:
| I seem to remember the Martin Fowler advice that this
| apparently cited being slightly more nuanced, and it's not
| totally wrong. If you have a simple enumerator and many switch
| statements depending on it then it is quite error prone if you
| have to add a value (depending on your language it may even be
| a silent failure), but a class will force you to implement the
| bits of behaviour you need as methods, and gives a good
| template for when you add another subclass. It's not always the
| right thing to do, you're trading the flexibility for others to
| add new types for the old flexibility of it being easy to add
| new behavioural decision based on a fixed set of types.
|
| I think in a lot of cases it's definitely worth stopping and
| thinking, and making sure you are implementing the right thing
| if you find yourself using enums heavily, or using a class
| hierarchy as an enumerator replacement.
| dwaltrip wrote:
| If I'm understanding correctly, languages like rust avoid
| that problem by throwing a compile error when a match
| statement doesn't cover all possible cases.
|
| Of course, a catch-all would then somewhat reintroduce the
| problem.
| aardvark179 wrote:
| Yes. The problem you then have is that adding additional
| values to the enum then either breaks compatibility or
| relaxes that guarantee. Since Java tries to run old code
| for as long as possible the only option for switch
| statements is to ensure there is a fallback so that you at
| least know there is a problem.
| dang wrote:
| Ok, but please don't post unsubstantive comments here. In
| particular, shallow dismissals and calling names are against
| the site guidelines. "A good critical comment teaches us
| something."
|
| https://news.ycombinator.com/newsguidelines.html
| userbinator wrote:
| Seeing sentences like that are very illuminating to explain why
| a lot of software turns into bloated monstrosities.
| bena wrote:
| I'd never heard of the phrase "frog in a well" before. I like
| it, I don't think I ever had a term for that phenomenon before.
| marcosdumay wrote:
| It's a reference to a story about a group of frogs that adapt
| to living a hellish life in a well, and when given the means
| to escape decide to destroy it instead of escaping.
|
| The story itself seems to be ancient, but I couldn't find
| where it originates from.
| Jensson wrote:
| I think it is originally from China, I see it a lot in
| Chinese stories and never seen it anywhere else (except
| here now).
| marcosdumay wrote:
| Hum... I know it from Portuguese literature.
|
| I was able to find some references from ancient India,
| but nothing about it coming from there.
| smegsicle wrote:
| "Kupa manduka is a Sanskrit phrase for the frog in the well
| that imagines the well, its home, to be the whole world and,
| therefore, becomes pompous. It is a term of derision for the
| intellectually complacent, for someone who thinks he knows
| everything there is to know."
| AndrewBissell wrote:
| Somehow I knew it must have been a dependency injection
| advocate who came up with the idea that enums are a code smell.
| quickthrower2 wrote:
| The more insidious problem is the card carrying believer in X
| saying "!X is harmful"
|
| See this pattern all the time in both technical and people
| side all the time.
|
| "You should never estimate using time"
|
| "Don't start new work until all the teams sprint commitments
| are completed"
|
| Etc.
|
| In a word it is arrogance with a twist of ignorance.
|
| Ignorance that complex systems of humans and code don't
| follow perfect dogmatic rules.
|
| It might be ok for junior devs to be a bit like this but
| always add a pinch of "most of the time, but when experienced
| you'll know when to ignore the rule"
|
| Ignoring some unwritten or written dogma is often what makes
| for a competitive advantage.
|
| Let your competitor drown in EnumClassFactorys .
| AndrewBissell wrote:
| Every one of these language toolbox books should come with
| a disclaimer for new devs up front about how the mark of a
| good craftsmen is knowing when to use the tools, when to
| bend or break their rules and guidelines, etc. I know it
| certainly might have saved me a fair bit of grief and oddly
| written code.
| userbinator wrote:
| But then they wouldn't sound "authoritative" enough. The
| dogma-cult that keeps its leader well-fed wouldn't
| survive.
|
| I think the best authoritative-sounding counter-quip is
| "Best practices are best not practiced."
| adamkl wrote:
| In Mark's defence, our views on software development can change
| over time as we learn and grow.
|
| Mark, for example, is now primarily a functional programmer and
| makes good use of F#'s type system:
|
| https://blog.ploeh.dk/2015/08/10/type-driven-development/
|
| So he might even agree with you these days (on the "baloney"
| part, but maybe not the "frog in a well").
| kaetemi wrote:
| That guy must be so afraid of bitfields.
| quickthrower2 wrote:
| "Should" is way too strong. "You should consider" is
| appropriate.
|
| An enum is essentially an encoding over integers. I guess this
| depends on your programming language, so humour me.
|
| If you have an integer whose meaning is a category (as opposed
| to a count or measure) - does this need refactoring into
| classes?
|
| Probably not most of the time.
|
| For example you import a CSV and one of the fields is a
| categorisation. Let's say county code. Your code may
| occasionally care if you are in an EU country.
|
| You could have a class per country and override isEU as needed
| but this is clumsy and ugly in my opinion.
|
| You'd also need a switch statement, in a factory method to
| create these from the csv data.
|
| A lot of the time it's easier to follow code where the switch
| statement is in its intuitive place.
|
| Highly OO abstracted code can be very hard to reason about too.
| dahfizz wrote:
| > Highly OO abstracted code can be very hard to reason about
| too.
|
| Definitely. Coming up with all the right design patterns and
| abstractions can feel quite clever and satisfying. But when
| someone else needs to debug that code, having to jump through
| layers and layers of object, interfaces, factories, etc to
| find out what a method call is actually doing is a pain.
| cle wrote:
| Can you elaborate on your shallow dismissal?
| dtech wrote:
| This _might_ have been valid in 00 's Java/C#. However, most
| modern languages and even Java 16+ have good Enum support and
| exhaustiveness checks, making them very convenient and safe
| to express a limited set of possible values.
|
| In contrast, an alternative like polymorphism + a visitor is
| safe, but excruciatingly verbose and hard to follow and
| modify.
| flaviu1 wrote:
| I'd expect it to go something like this:
|
| - There are many many languages without classes that make
| good use of enums
|
| - Doing this refactoring is excessively verbose
|
| - Someone making bold claims like this about a language
| feature that's never been considered error prone and been
| around since the beginning ought to provide some really good
| evidence to back things up.
|
| There's cases where enums are good, and there's cases where
| polymorphic classes are good. Dismissing one of them by
| default is wrong, since both are useful at different times.
|
| edit: and the citation there to Martin Fowler et al.,
| Refactoring: Improving the Design of Existing Code doesn't
| even match the claim being made. That book mentions
|
| - "The first part of this problem is that switch statement.
| It is a bad idea to do a switch based on an attribute of
| another object. If you must use a switch statement, it should
| be on your own data, not on someone else's." -- sure, I agree
|
| - "Often you find the same switch statement scattered about a
| program in different places. If you add a new clause to the
| switch, you have to find all these switch, statements and
| change them. The object-oriented notion of polymorphism gives
| you an elegant way to deal with this problem." -- fair enough
|
| This is a long way from "enums are a code smell," and
| honestly feels like padding the citation count. Another thing
| to note is that the second edition of Refactoring: Improving
| the Design of Existing Code says:
|
| > Even in our more wild-eyed youth, we were never
| unconditionally opposed to the conditional. Indeed, the first
| edition of this book had a smell entitled "switch
| statements." The smell was there because in the late 90's we
| found polymorphism sadly underappreciated, and saw benefit in
| getting people to switch over.
|
| > These days there is more polymorphism about, and it isn't
| the simple red flag that it often was fifteen years ago.
| oflannabhra wrote:
| In Swift specifically, switch statements are exhaustive, so
| adding a new case to an enumeration that is switched on
| will automatically give you compilation errors at every
| point in the code base where that enum is matched against.
|
| Which, in my experience is a huge advantage rather than the
| supposed disadvantage Fowler is saying it is (although I
| could see that being the case in many other languages).
| bryanrasmussen wrote:
| >This is a long way from "enums are a code smell,"
|
| originally the meaning of code smell was not that code that
| had the smell was bad, but there was a chance it was and
| should be examined. For this reason of course it is useful
| to get rid of code smells so that people don't feel the
| need to investigate hey is this smelly code actually bad
| code.
|
| But all that said not sure if an enum is a code smell.
|
| on edit: this at any rate was the rationale behind the
| phrase code smell I was first introduced to.
| masklinn wrote:
| > - "Often you find the same switch statement scattered
| about a program in different places. If you add a new
| clause to the switch, you have to find all these switch,
| statements and change them. The object-oriented notion of
| polymorphism gives you an elegant way to deal with this
| problem." -- fair enough
|
| Even that seems like it could easily be way overkill e.g.
| if you have multiple switches which, say, generate a label
| from an enum, the first step is probably to add a utility
| function / method, not to migrate the whole thing over to
| polymorphism.
|
| Although there is one thing to be said about _context_ :
|
| * OP works in C#, whose enums are literally useless
| (they're like C's)
|
| * apparently even in Java (which at least has type-safe
| enums even if not sum types), `switch` is unable to check
| for completeness
| aardvark179 wrote:
| Switch statements are not required to be exhaustive, but
| switch expressions are. I'd have to check but I think it
| was found that changing the exhaustiveness requirement
| would break too much source code. The compiler will still
| insert some some sort of default case because somebody
| might add to the enumerator, and it might be in a
| separate compilation unit.
| couchand wrote:
| Generally the issue with the switches scattered about (or
| more generally, and concern about conditions scattered
| about the codebase) is that the body of each case is
| different. Of course if all the bodies are the same the
| easier option is a utility method.
| jaywalk wrote:
| As someone who works in C# and finds enums useful, I'd be
| curious to know why you describe them as literally
| useless.
| masklinn wrote:
| A C# enum is, like a C enum (as they were explicitly
| introduced to be compatible with those), just a bunch of
| constants for integers.
|
| So when you have an enum-typed value, odds are good that
| it's one of the named ones but there's no mechanism
| anywhere preventing it to be any other integer of the
| underlying type.
| Semaphor wrote:
| But how is that an issue? I guess when you are casting
| random integer to the enum-type without any checks?
| masklinn wrote:
| > But how is that an issue? I guess when you are casting
| random integer to the enum-type without any checks?
|
| Any caller can send any garbage (if you're publishing a
| package / API), likewise a dependency can return any
| garbage, etc... C#'s enums are entirely indicative.
| strulovich wrote:
| For me it's the following: - going for polymorphic classes
| instead of a few switches can cause bad coupling of
| knowledge. ( For example, instead of a switch on what to log
| in module A and the same for module B, now you're mixing code
| from these two modules due to a shared constant.
|
| Sometimes these polymorphic classes make sense, but my
| experience is that it's a minority, and it's better to err on
| the side of some simple enums.
|
| Also note that Uncle Bob in Clean Code pretty much directly
| opposes in one of the chapters about logic and data
| separation (can't find the chapter right now since I don't
| have the book anymore)
| kaetemi wrote:
| The OO story generally falls apart once you go outside of the
| module/library, and want to actually treat objects like
| generic objects, and have new custom behaviour for some types
| of objects.
|
| Either you end up with 'scary' enums, or with dynamic_cast
| and 'scary' null pointers.
| mannykannot wrote:
| Fivelessminutes's dismissal is indeed shallow, but then
| again, the rule being dismissed looks like shallow dogma
| (though we are not seeing it in context.)
|
| Enums are essentially categorical variables, and there is
| nothing wrong with that: they are widely used in statistics
| and other real-world applications (arguably, even, booleans
| are a case of such.)
|
| As a junior programmer, I was once assigned to work on an
| example of what could happen as a result of following this
| advice: an explosion of subclasses for the sake of avoiding a
| few conditional clauses. Algorithms had been broken up so
| that the common parts could be put in base classes, and you
| had to jump all around the code to see what was going on.
| Worse, objects could change their categorization during their
| lifetime, which required the substitution of a new object for
| the previous one. This complexity was contagious; once this
| one type was implemented in this manner, a lot of other
| things had to follow suit.
|
| This was one of the experiences that turned me into a skeptic
| with regard to the proposition that there's no problem that
| cannot be simply solved by being more object-oriented.
| coldacid wrote:
| Mark Seemann started it first.
| mcraiha wrote:
| It is also good to remember that different programming languages
| have different features for enums. e.g. in some languages you
| cannot print the enum as string, and in some languages you cannot
| choose the underlying type of the enum.
| notacoward wrote:
| The key IMO is mentioned in one of the comments.
|
| > [lots of] switches on enums are a code smell
|
| Someone else also pointed out that a code smell doesn't mean
| something is _always_ bad - only that it 's _likely_ to be bad.
| An enum that 's rarely or never the subject of an if/switch check
| or pattern match is probably OK. For example...
|
| * A type-safe and self-describing alternative to a boolean
| argument
|
| * A scheduler priority or group
|
| * A state in a state machine
|
| OTOH, an enum that appears often in conditionals is likely to be
| error prone and classes or interfaces should be considered
| instead. But it's really all pretty context-dependent. The
| question is: which approach creates more coupling and/or requires
| more points in the code to be updated to remain correct when a
| new value is added?
| trinovantes wrote:
| Never knew enums are even considered to be code smell
|
| Linters can/should already detect when a switch on enum doesn't
| handle all cases
| chris37879 wrote:
| They're only a code smell in languages that treat them poorly.
|
| Most modern languages have enums as just a matter of course for
| the language. I'm using Dart rather a lot these days for
| Flutter, and I absolutely love their enums. I can extend one
| and use it like a pseudo type union, or for a thing like
| `Environment.development`, I can extend that so that any
| `Environment` variable automatically has properties like
| `apiUri` or `name` or whatever other thing that changes per
| environment, but the set of them is still well known ahead of
| time.
| Lutger wrote:
| For some languages, like D, that can also be expressed in the
| language itself: https://dlang.org/spec/statement.html#final-
| switch-statement
| IggleSniggle wrote:
| Exactly. Indeed, the whole point of using switch+enum is
| frequently to get exhaustiveness checking on your conditional!
| klyrs wrote:
| Me neither, but now I'm picturing a horrible mess of an IOCCC
| entry that abuses enums beyond all reason...
| jeffbee wrote:
| Aside from linters, at least some very common C++ compilers
| will refuse to compile a non-exhaustive switch statement.
| todd8 wrote:
| Do you mean when specific flags are passed to the compiler? I
| wasn't aware of that. C++ has defined behavior for non-
| exhaustive switch statements, just like a non-exhaustive if
| statement (that is an if statement missing an else clause).
| gwbas1c wrote:
| Or in a unit test
| bigbillheck wrote:
| I really hope that 'enums are a code smell' is the worst
| technical opinion I see today because I'm not sure I could take
| much worse.
| gwbas1c wrote:
| In C#, enums basically add typing to a predefined set of entities
| or statuses. This is why they basically are just numbers under
| the hood.
|
| Practically, though, they are important when dealing with
| serialization: Writing state to disk, database, or over the wire.
| Because they quickly typecast to a number or a string, you don't
| have to deal with the complexity of serializing or deserialzing.
|
| Having recently inherited a C# codebase where enums weren't used,
| what I can say is that "polymorphic classes" to represent a
| predefined set of things is a smell. This manifests itself in
| cases where we had tables with a limited set of constants, and
| were loading them through joins. It was much more efficient to
| remove the join and turn the foreign key into an enum. In another
| case, the "object" is fully serialized in a contract to JSON.
| It's much more efficient to just serialize a single value in the
| contract as well.
| stathibus wrote:
| This is a great example of why the "clean code" movement is so
| destructive. Writers (bloggers, textbook authors, professors,
| whatever) can very easily take advantage of inexperienced
| developers who are dying for someone to tell them what is and
| isn't the right way to do things, and virtually all "code smell"
| style blanket rules are detrimental to learning. In this case and
| in many other cases, the rules doesn't tie back in any way to
| what the code actually needs to do.
| brazzy wrote:
| _laughs in Java, where enums ARE polymorphic classes_
| zzbzq wrote:
| The only time they ARE a code smell is when the values are only
| used one place and they could have been in-lined for readability.
___________________________________________________________________
(page generated 2021-11-22 23:02 UTC)