paint-brush
Rethinking JavaScript: Eliminate the switch statement for better codeby@joelthoms
57,584 reads
57,584 reads

Rethinking JavaScript: Eliminate the switch statement for better code

by Joel ThomsJanuary 12th, 2017
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

In my last 3 articles I convinced you to <a href="https://medium.com/p/rethinking-javascript-the-if-statement-b158a61cd6cb" target="_blank">remove the if statement</a>, <a href="https://hackernoon.com/rethinking-javascript-death-of-the-for-loop-c431564c84a8" target="_blank">kill the for loop</a>, and <a href="https://hackernoon.com/rethinking-javascript-break-is-the-goto-of-loops-51b27b1c85f8" target="_blank">never use break</a>. Now I’m going try to convince you to eliminate the switch statement.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Rethinking JavaScript: Eliminate the switch statement for better code
Joel Thoms HackerNoon profile picture

In my last 3 articles I convinced you to remove the if statement, kill the for loop, and never use break. Now I’m going try to convince you to eliminate the switch statement.

While the switch statement is useful, it doesn’t quite fit in with the rest of our functional code. It’s not Immutable, it can’t be composed with other functions, and it’s a little side effecty.

Switch also uses break, which is also anti-functional. (Find out why break is so horrible in the article below)


Rethinking JavaScript: Replace break by going functional_In my last article, Death of the for Loop, I tried to convince you to abandon the for loop for a more functional…_hackernoon.com

What I really want is something immutable and free of side effects. Something I can pass one value in and get one value out. You know, something functional!

Let’s dig right in and take a look at an example straight from the redux website.










function counter(state = 0, action) {switch (action.type) {case 'INCREMENT':return state + 1case 'DECREMENT':return state - 1default:return state}}

I tried playing with this a bit and ended up converting it to a nested ternary.




const counter = (state = 0, action) =>action.type === 'INCREMENT' ? state + 1: action.type === 'DECREMENT' ? state - 1: state

(If you find this syntax confusing, then check out this article, it will explain if's and nested ternary.)


Rethinking JavaScript: The if statement_Thinking functionally has opened my mind about programming._medium.com

This is better, but one thing I didn’t like was repetition of action.type ===. So then I thought I could probably use an object literal. I could also create a function to wrap the object literal and at the same time add support for a default case.







function switchcase (cases, defaultCase, key) {if (cases.hasOwnProperty(key)) {return cases[key]} else {return defaultCase}}

Now let’s curry this bad boy, convert the if to a ternary and ES6 it up a bit!


const switchcase = cases => defaultCase => key =>cases.hasOwnProperty(key) ? cases[key] : defaultCase

Sexy!

Now our reducer would look like this…





const counter = (state = 0, action) =>switchcase({'INCREMENT': state + 1,'DECREMENT': state -1})(state)(action.type)

The problem with this early version of switchcase is the entire object literal is evaluated before being passed to the switchcase function. This means state +1 and state -1 are both evaluated. This can be very dangerous… No bueno.

Hmm… if the values in the object literal were functions, then they could be executed only when there is a matching case.

So let’s create a companion function called switchcaseF (F is for function), which is just like switchcase except it’s values are functions.


const switchcaseF = cases => defaultCase => key =>switchcase(cases)(defaultCase)(key)()

Now the reducer’s values can be changed to functions and only the matching case will be evaluated.





const counter = (state = 0, action) =>switchcaseF({'INCREMENT': () => state + 1,'DECREMENT': () => state -1})(() => state)(action.type)

…but I kinda want these functions to be optional. So let’s try adding this…


const executeIfFunction = f =>f instanceof Function ? f() : f


const switchcaseF = cases => defaultCase => key =>executeIfFunction(switchcase(cases)(defaultCase)(key))

Now our redux reducer can look like this (notice the default value)…





const counter = (state = 0, action) =>switchcaseF({'INCREMENT': () => state + 1,'DECREMENT': () => state -1})(state)(action.type)

OK, not vastly different, but we could do stuff like this. (added a RESET)






const counter = (state = 0, action) =>switchcaseF({'RESET': 0,'INCREMENT': () => state + 1,'DECREMENT': () => state -1})(state)(action.type)

Another Example

This example is taken from http://www.w3schools.com/js/js_switch.asp

var day;






















switch (new Date().getDay()) {case 0:day = "Sunday";break;case 1:day = "Monday";break;case 2:day = "Tuesday";break;case 3:day = "Wednesday";break;case 4:day = "Thursday";break;case 5:day = "Friday";break;case 6:day = "Saturday";}

Because we have properly curried switchcase like good boys and girls we can decompose this switch to multiple reusable methods, and we all know…

making our code reusable should always be one of our goals.









const getDay = switchcase({0: 'Sunday',1: 'Monday',2: 'Tuesday',3: 'Wednesday',4: 'Thursday',5: 'Friday',6: 'Saturday'})('Unknown')


const getCurrentDay = () =>getDay(new Date().getDay())

const day = getCurrentDay()

Here we have separated out all the logic from our impure getCurrentDay function into a pure getDay function. This will make testing and function composition much easier.


Functional JavaScript: Function Composition For Every Day Use._Function composition has got to be my favorite part of functional programming. I hope to provide you with a good real…_hackernoon.com

Extra Credit

Now we can do cool things like create a switchcase programatically!

const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']




const getDay = switchcase(days.reduce((acc, value) =>(acc[value] = `The day is ${value}.`, acc), {}))("I don't know what day it is.")

console.log(getDay('Monday'))

// 'The day is Monday.

This isn’t the greatest example. There are obvious and better ways to generate this sentence, I’m just trying to demo what the switchcase can do, so cut me some slack!

The Code

Here’s our final, pure, immutable and functional switchcase code.

3rd Party Libraries

For those interested in digging deeper into the functional world, there are some great libraries out there I would like to recommend.

Ramda — Ramda is a functional library similar to lodash and underscore. Check out Ramda’s cond function. The functionality if similar to this switchcase and a great alternative!

lodash/fp — lodash/fp is the functional version of lodash. It’s worth taking a look.

Fantasy Land — One of the more popular functional libraries for JavaScript.

Summary

The switch statement doesn’t fit into our functional paradigm, but we can create a function that has similar characteristics. Doing so let’s us decompose the switch into smaller and reusable pieces. We can even generate one programatically!

I know it’s a small thing, but it makes my day when I get those follow notifications on Medium and Twitter (@joelnet). Or if you think I’m full of shit, tell me in the comments below.

Cheers!

note: before you flip out about switchcase not supporting ‘fall-through’, this is because I didn’t have a need for it in my project. It’s not meant to be an exact replica of switch. YAGNI or something.

Related Articles


Rethinking JavaScript: Death of the For Loop_JavaScript’s for loop has served us well, but it is now obsolete and should be retired in favor of newer functional…_hackernoon.com


Functional JavaScript: Resolving Promises Sequentially_I love the new Promise library that ships with ES6, though one thing has been left out, a function to sequentially…_medium.com