Writing flat & declarative code

Auteur
Peeke Kuepers
Datum

Bringing a bit of functional programming to your codebase

Recently I have picked up an interest in functional programming. Its concepts fascinate me: applying math to enable strong abstractions and forcing purity to avoid side effects and enable good reusability of code. I also find it incredibly complex.

Functional programming can have an incredibly steep learning curve. With it’s roots in the mathematical category theory it doesn’t take long before you encounter terms like identities, functors, profunctors, monads, contravariants and so on. I’m not even close to understanding all these concepts and maybe that was why I never put functional programming into practice. I’m sure many of us can relate.

It got me thinking: might there be some intermediate form between regular — imperative — code and full-on functional code? A form allowing us to apply some functional goodness to our codebase, whilst leaving the complex stuff out for the time being?

The best feature of functional programming to me, is that it forces you to write declarative code: code that describes what you do, as opposed to describing how stuff works. This means that you can easily understand what a specific block of code does, without understanding exactly how it works. As it turns out, writing your code declaratively is one of the easiest parts of functional programming!

Loops

…a loop is an imperative control structure that’s hard to reuse and difficult to plug in to other operations. In addition, it implies code that’s constantly changing or mutating in response to new iterations. — Luis Atencio

So let’s start by taking a closer look at loops. Loops are a great example of imperative code (the opposite of declarative code). Loops involve a lot of syntax describing how their behaviour works instead of what they do. In the process data is mutated. This is unwanted because mutation is impure. The array may be used somewhere else in your code as well where the mutation can lead to unexpected results. The main complaint though, is that code using for loops is difficult to comprehend at first sight. It’s written imperatively. The for loop tells us how we loop over the array, not what we wish to achieve.

We can easily rewrite loops in a declarative way by using array methods. Array methods directly convey what they do. Well-known array functions include: forEach, map, filter, reduce and slice (there are a handful of extra functions, especially considering ES6 and 7, but these are the most used ones).

Take a look at the code sample below:  

function helloworld(arr) {

  const evenNumbers = n => n => n % 2 === 0    
  const double = n => n * 2

  arr
    .filter(evenNumbers)
    .map(double)
    .forEach(doSomething)

  }

Using these functions you have a clear signaling of intent. Another programmer doesn’t have to understand exactly how your program works to understand what it does. It helps keeping your code simple and readable.

If else statements

So what about our beloved if else statements. If else statements are another great example of imperative code. To make your code more descriptive, you can use ternary statements. Ternary statements are exceptionally useful when defining variables (or returning values) with the conditional value as a const. Using an if else statement would confine the use of the variable to the scope of the statement. By using a ternary statement we avoid this problem:  

const condition = true;

if (condition) {
  const a = 'foo';
} else {
  const a = 'bar';
}
const b = condition ? 'foo' : 'bar';

console.log(a); // Uncaught ReferenceError: a is not defined
console.log(b); // 'bar'

Converting an if statement to a ternary one forces us to move the condition to it’s own variable. This way we can fit the ternary on a single line and as a welcome side effect we now communicate what the boolean value represents through the name of the variable. Nice!

Events

Finally, let’s take a look at event handling. Event handling is traditionally difficult to write in a flat fashion. With regular callback driven functions you could promisify them to enable a chained, flat style of programming. Promises only resolve once though, and most events are definitely going to fire multiple times. There’s a pretty good way to handle event handling declaratively by using a concept called the Observable. Think of an Observable as a Promise that can resolve multiple times. The Observable type can be used to model push-based data sources such as DOM events, timer intervals, and sockets. The Observable is currently a stage 1 proposal which you can find over at GitHub. In the code below the listen function returns an Observable. As you can see, we’re able write an entire AutoComplete class using only a single method chain of five functions:  

import { apiCall, listen } from 'helpers';
import { renderItems } from 'templates'; 

function AutoComplete ({ endpoint, threshold, input, container }) {

  listen(input, 'input')
    .map(e => e.target.value)
    .filter(value => value.length >= threshold)
    .forEach(value => apiCall(endpoint, { value }))
    .then(items => renderItems(items))
    .then(html => container.innerHTML = html)
}

 
I’m really thrilled with Observables coming to ES itself, since most library implementations — like RxJS — are way too large for my liking. The map, filter and forEach methods in the code above aren’t part of the spec yet, but are implemented in the extended API of the zen-observable implementation of ES Observables (which happens to be really light as well!).


I hope I’ve managed to interest you in some of these ‘flat’ patterns. Personally, I’ve really enjoyed rewriting my programs this way. Every bit of code you touch instantly gets more readable. And the more experience you gain with this technique, the more situations you recognize it is applicable. Just remember this simple rule of thumb:  

The flatter the better!

 

If you’ve got any questions or want to discuss further, please do! You can email me at pkuepers@mirabeau.nl or hit me up on Twitter @peeke__.

This blog article is a simplified edit of https://peeke.nl/writing-flat-code. If you’d like to see practical examples of these concepts you should read my original post.

Tags

Development