I sort of know that switch statements are typically considered a good idea when chaining together a lot of else if
statements. I'm not against this refactor in any big way, but I've also never found it overly important.
I tend to find the behavior and syntax of if
and else
to be pretty consistent regardless of language compared with switch statements so reaching for if
is easier, but even when chained doesn't seem that much less clean than switches.
I definitely agree that getting rid of if
..else
..else
chains are worth refactoring away, but I haven't found that a switch statement actually improves the quality meaningfully.
Thoughts?
Top comments (81)
That's why we never have
switch
in Python 😁And, the dictionary is always a better and cleaner alternative for the ugly switch.
Elixir :)
Pattern matching to the rescue ?
Isn't this feature called method overloading ? 🤔
No, because overloading is have same parameters with different types, but in patter matching you can have the same type and number of parameters but differs in the content. For example, here!
I quite like cond as well for certain situations as well.
Whenever I think about why I don't like a certain way a language does something, I realize it always goes back to my love for Python. That language has ruined me. haha
❤ for the utmost ruining lang.
How would you handle a
default
case in this?My though is to add
|| defaultValue
at the end since a failure to match any property will returnundefined
. Thoughts?That would be handy using a try except:
I added the extra
default
variable just to make the code self explanatory.EDIT: Bad solution, look at the other comments.
You can also use defaultdict in the standard library :D
This is clean :D
This is the answer to this entire thread.
@rhymes and @pedromendes96 got it better than me 😁
Really love this pattern. I have seen various approaches to the
default
case that vary across languages.The main detractor with
if
statements is that you have to reduce your logic down to booleans, which can later be hard to trace through. But,switch
statements do not go far enough to help the situation either. Reducing your logic down to integers or strings is not that much better of an abstraction. Both force you to reduce the expressiveness of your solution down to a primitive level.This is a reason I am a big fan of Union types, which allow you to explicitly model your logically different cases along with their different related data. Combined with pattern matching, you can branch off those logical cases as-is. My favorite language which supports this is F#.
Kasey, I have a question for you since I’m interested in F#. I use union types all of the time in TypeScript, but I often “unpack” (for a lack of a better word) the union by using an if statement. It’s really convenient since TypeScript has excellent control flow analysis and therefore discriminates the union so you know that it is only the one type.
So do you still prefer the functional approach for discriminating the union even in languages that can clarify the resulting type?
(I’m sorry if this question isn’t more clear. I suppose I’m suggesting that if statements are fine since I think the left/right monad thing very confusing)
Typescript union types are a bit different from those in F#. There is a tradeoff made between them. Union declarations are more or less anonymous in Typescript, whereas F# requires you to create a Discriminated Union with explicit tags. So, the Typescript feels a little nicer on declaration. However, when you unpack the union type, I believe F# has the advantage. Using
match
you can get auto-completion of the cases and warning when all cases are not exhaustively matched. It also provides access to the value as the appropriate type.Examples:
So, I feel like the latter is a better expression of intent in the long run. Even though it requires an extra type declaration, the type declaration adds some value for readability. It is more clear what the code is going to do with "string | number" when labeled as Text and Spaces.
For F#, I also swapped the order of arguments so that
padLeft
would be chainable with other string functions using pipe (|>
).What a wonderful response. Thank you for being generous with your time to write that thoughtful response. You should post that as it’s own article so more people can benefit from it. :)
For me its about using the semantics of the language to communicate as much information as possible to other developers (and my future self). Its the same reason I prefer
map
,filter
,reduce
overfor
loops. By usingswitch
instead ofelse if
, I communicate a more specific action is taking place: dispatch over a single value. It's a small thing, but its also easy to do and makes my code a little more compact, so why not?Having the fallthrough can be nice for certain cases when you want to keep your code DRY or want to avoid a ton of
||
operators in yourif
statement.The
switch
operator can be much cleaner thanif
though, notably for small return statements. For example:is much cleaner than
Of course, it does come down to personal taste, but since languages provide the operator in the first place there's obviously a reason for it.
If you're returning then you don't even need the else.
Actually looks a bit tidier to me and still very explicit.
Depends on the language, but for safety critical code, multiple function exits are typically frowned upon.
Nah. Exit early. Makes the code cleaner and clearer.
Gotta respectfully throw out a dispute. I've heard this statement used to justify multiple returns before... Looks like
With the justification that it avoids evaluating the additional conditionals if the first proves true.
I just don't think the performance loss is there barring some REALLY funky conditionals or some other code smell...
This is a rule that feels like it came from the old C days and just stuck around past it's expiration date, because that same logic, but expressed as
Is so much more readable and you can look at one line to know what's going to be returned, then you only have to debug/watch one thing, and it's just SO much cleaner... If I run into a problem with evaluating conditionals to get syntax that clean, then I feel like something else is wrong in the code... It's also more bug resistant and ensures that the method is doing ONE thing. Less chance that someone can come along and decide to start returning Strings instead of ints in one of the return statements or something wacky like that...
In the real situations I encounter the issue is that having early returns means being able to avoid huge indents. When reading code where the return is at the bottom of a long complex structure you are forced to parse the entire function to ensure there is no subsequent operation which influences the return value. Having one return value generally means having the return variable declaration at the highest level, and so it could potentially be modified through the function. You might intend for it not to be modified once set, but a later modification might overwrite it. A early return is syntactically a succinct expression of the intent. We are finished. Return this value now. I know that in the early days of programming there was a good reason not to return early, especially in the middle of a loop. Don't think there is any issue at all with this any more.
I could easily see if I'm dealing with a returnable statement over 20 lines where the return value is based on multiple conditionals or is impossible to keep in your head all at once, that return early and often would be a good backstop to make sure you know what is returned and when... And I HAVE run into code like this in the wild that I couldn't refactor entirely for stability, but I could make sure it was readable, and that did involve applying Exit Early.
With new code or things I could refactor, I would have to go with Uncle Bob's rule from Clean Code
If you're dealing with multiple nested conditionals or the triangle of death where nesting is making the indents move out and out until your code looks like an arrow pointing off to the right, then the function is probably just too big and needs to be broken up for maintainability...
But now we're going to get so far off the topic we'll need to write a whole new topic to keep this up hahaha!
I broadly agree with the idea that functions should be small, but care should be taken not to break up logically sequential operations that really belong together just to comply with this principle.
More important to me is that a function should have one clearly defined purpose and not mash together a bunch of logically separate operations.
Switches tend to be used for doing multiple separate things based on some value. It is not always bad, but I always evaluate whether there is a better more extensible way to do it than a switch when I see it.
I'm not saying I've never used a switch, but it is generally not one I use unless there is a really good reason.
Ah, true. I'm an idiot.
But for me, I still prefer the inline look of the switch statement. Again it's up to preference though.
I don’t think fallthrough in a switch case is ever appropriate due to the potential for buggy side effects. I describe that in this article and I’d love to hear your thoughts on if you agree: dev.to/cubiclebuddha/is-defensive-...
I absolutely agree with your point.
default
/else
cases should be used for error handling in most instances.However, I should have clarified, so the fault is on my behalf, but when I said "switch 'fallthrough'", I meant when you have a situation like the following:
Here, it's easier (for me, at least) to see which possible values return 1 than this:
I'm probably missing another thing you can do with
if
statements to make it more clear. But, after all, this is #healthydebate... So tell me how I'm wrong! :PThose examples your provided look great to me. I just don’t like when code assumes that there will never be a new case. I explain that a little better in my article. But it looks like you’re covering the known cases. So great work! :)
Switch
es as well as conditionals with many branches(else if
s) are generally indicative of a different issue all together.Switch
es can be a good choice in the case of a clearly defined unchangeable list of things to check against, like days of the week(I pray to god there will only be seven in the future too). They are also sometimes good in the case that you actually have found a use case for the fall through effect you get when you don't use abreak
.Switch
es also are very hard to do in a way that enables defensive programming. It's easy for aswitch
to have 4 or 5 cases, then someone comes in and adds one, comes in and adds another, then another, until you have a giant mess ofcase
s.Generally, like @rossholloway94 mentioned,
Enum
s are a much better fit. The way that @0xrumple showed in python can be done in many other languages that don't have realEnum
s, but is essentially anEnum
as well. Alternatively, in certain use cases, something like a rule matcher and handler can be a better fit.Using a
switch
can also be indicative that whatever logic you're doing in theswitch
, is really something that should be on each class to implement which can be similar to the model above. Basically the Strategy design pattern.The reason a
switch
never feels better than manyelse if
s is because they are basically the exact same thing. The kind of feel that gets hammered in a lot of beginner tutorials that explainswitch
es is something like "If you have a lot of conditions, use aswitch
!" and also the examples show how to convert a thing with a lot ofelse if
statements into aswitch
.One more time for clarity:
switch
alternative candidates are:Enum
sInterface
s in classesEnums are also good, but they might not fit in cases where there is no ordinal values.
Don't get offended, but the rule handler matcher / OOP ways to return a value reminds me of this meme:
Haha no offense at all. The rule handler is overkill in A LOT if not most scenarios. That said, it's for sure one of the patterns you would use instead of a switch so I included it. For the trivial example I have though, yeah way overkill. For the slackbot I made a while back I used it and it worked well. github.com/samuraiseoul/starbot/bl... and github.com/samuraiseoul/starbot/bl... are an example where it works well.
Aha... Heck yeah, this example is pretty solid 👌
Excellent point! Sometimes you may need lots of
if
statements, but more often than not, lots ofif
s are a sign that your code is just too procedural. By abstracting away code in functions or classes you can often get rid of them.Personally, I oppose switch statements because they force you to opt-in to
break
rather than breaking by default (i.e. usecontinue
for fallthrough). I feel like every time I write a switch statement I forget abreak
on one of the cases. Then, I'm stuck exposing that fallthrough case during testing. In contrast, you don't really get that same ambiguity withif
/else if
/else
.In terms of performance, it never practically matters. For sake of the argument, switch is much faster if you have a large number of cases and slightly slower with small number of cases. The compiler is aware of the number of cases and will convert the switch statement into a constant lookup time hash table once it exceeds a threshold. But once again, for 99.999% of code, the difference between the two will never be meaningful.
I personally think that they are used differently, and that using them both is actually the best option. If/Else can be used very broadly, but with switch it's pretty niche. It's essentially a jump table that's useful when you have a ton of outcomes given some input. When I see a switch, I immediately know the class of problem it's trying to solve. With if/else, it could be much broader.
A good optimiser will use the optimal output for the size of the operation, irrelevant of the code statement.
I'm sorry but this is a really uneducated comment.
Swift's
switch
is the only one that I think gets it right. It provides enough simplicity and usability to warrant use over if-else chains.From docs.swift.org/swift-book/Language...
case "a", "A":
case 1..<5:
switch point: case (0, 0)
case let(x, y)
where
checksfallthrough
Granted, each of these can still be done with if-else, I like the convenience of it all.
This here.
switch
in some languages (like F#, rust, and apparently swift- amongst others) has additional "powers".In C# 8 you can also use it as an expression:
Otherwise it's main advantage is clarity for large numbers of options and optional fall through (double-edged sword that it is).
Regardless what you do with your brackets/whitespace, the switch logic is clearly only about the value of
x
.Also, historically compilers were more likely to turn it into a jump table rather than chain of branches.
Considering it rapidly speeds up your conditional statements, I will always use a switch statement. A switch statement acts as though you are accessing an array via an index. Whereas an elseif statement has to process each conditional statement until it passes.
Depends on if scripted Vs compiled.
A compiler will optimise into either the conditions or a jump table depending on the code. Usually for both switch and if else
As someone who has never had the chance to jump into a compiled language, this is wonderful information! Thank you for the correction.
Of course this depends on the context of the application though. If I am checking against static values, then my previous statement is true.
I much prefer pattern matching like in F# to switch statements. Eevee did an interesting blog on the history of the switch statement. I dislike the fallthrough behavior and find it visually displeasing in a code base.
Thanks for the article! That was a quality read. Gave me a more clear explanation of why I don’t really like switch statements. Haha