Inter Caetera

4/8/2021

Three intermediate functional JS patterns

Purely functional programming in JS is quite difficult if not impossible. This is primarily due to the lack of language features we come to expect from functional languages like Elixir, features such as conditional expressions or immutable collection manipulation functions.

The truth, though, is that JS has those, but they are quite arbitrary and not as robust as their equivalents in Elixir. However, we can manually work around them with some creativity.

Pattern one: rest removal

The situation: we have an object and we want to immutably remove some of its properties (i.e. receive a new object with some properties removed). JS has a keyword for this: delete but it mutates the object, so it’s no good for our purposes.

Languages like Elixir have methods like Map.drop/2 that do this explicitly. In JS, however, we can use the ... rest operator and object destructuring to get rid of the properties we don’t want.

const author = { name: 'Anna', surname: 'Komnene', dob: 1083 }
const { dob, ...authorWithoutDob } = author
console.log(authorWithoutDob) // => { name: 'Anna', surname: 'Komnene' }

Also, if you use this pattern with ESLint, you might find the ignoreRestSiblings option for no-unused-vars quite useful.

Pattern two: logging in arrow functions

The console object in JS is the most powerful tool in your debugging toolbelt and it’s worth knowing how it works, primarily because it effectively eliminates the need for a dedicated debugger. When you’re doing more functional programming in JS you will very often use the feature of arrow functions that immediately allows you to return a value:

const plusTwo = x => x + 2

To inspect the value received into this function normally would be quite difficult because we would have to wrap the function in curly braces, put in a console.log and explicitly return the value. Too many keystrokes, and difficult to undo in Vim!

Thankfully, console.log (and other console functions) always return undefined, which is a falsey value. We can combine it with the or (||) operator to inject a console log before the return value.

const plusTwo = x => console.log(x) || x + 2

This pattern is most useful when inspecting values coming in from fetches and other asynchronous requests, as well as in Redux actions.

Pattern three: avoiding nested ternaries with functions

Let’s say we have to create a variable whose value is computed conditionally from another value. At first it might be tempting to use the ternary operator:

const distanceInKm = 30
const deliveryPrice = distanceInKm > 15 ? 3.99 : 5.99

However, this becomes problematic when we have multiple conditions.

const distanceInKm = 30
const deliveryPrice = distanceInKm > 15 ? (distanceInKm > 30 ? 8.99 : 5.99) : 3.99

This is very difficult to read, though, and doesn’t scale for even more conditions. At this point, we might be tempted to use a switch statement or an if, but these are not expressions (like in Elixir) so we would have to convert our variable to let:

const distanceInKm = 30

let deliveryPrice
if (distanceInKm > 30) deliveryPrice = 8.99
else if (distanceInKm > 15) deliveryPrice = 5.99
else deliveryPrice = 3.99

However, in functional-style JS, let is lava because it opens up our value to be accidentally reassigned later down the line and it will not be caught by the linter or the interpreter. The solution is to refactor our conditional out to a function (and as a bonus we can use something approximating guard clauses by returning from ifs):

const getDeliveryPrice = distance => {
	if (distance > 30) return 8.99
	if (distance > 15) return 5.99
	return 3.99
}

const distanceInKm = 30
const deliveryPrice = getDeliveryPrice(distanceInKm)

Summary

I picked these patterns because I use each one of them at least a couple of times a day when working on real-life commercial projects and I still find people who are surprised when I point these out. Hence this post — I hope it has been useful for you as well.

Divider Divider
Back to top