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)
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
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!
Here’s our final, pure, immutable and functional switchcase
code.
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.
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.
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