Better Programming

Advice for programmers.

Follow publication

Write Delightful Declarative JavaScript

Jayson Alzate
Better Programming
Published in
8 min readJan 19, 2021

person working at a laptop
Photo by Christina @ wocintechchat.com on Unsplash

I recently have been going down a rabbit hole of functional programming after dabbling with it for a couple of years. Functional programming has a ton of advantages. This includes functional purity (i.e., same results every time), which makes your functions easier to test. Hands down, though, the benefit I love most from functional programming is the readability gained from composing small functions together. This style is known as declarative programming.

In this article, I wanted to explore the concept of declarative programming broadly and what techniques and concepts can allow a developer to start introducing it into their codebase. While the article is grounded in some functional programming concepts and techniques, the focus is not on being militant about enforcing functional programming conventions. In my opinion, readable code begets functional code!

My hope is that by the end of this article, you should have a sense of what declarative programming is, learn some concepts to help you begin writing more declaratively today, and learn a bit about Ramda, a popular functional programming library.

Before we begin, let’s dive into what is declarative programming and how it differs from the more common imperative programming style.

I Do Declare: Declarative vs. Imperative Programming

The difference between these two programming styles is usually defined as follows.

the word “declarative” defined as “what you want to do” and the word “imperative” defined as “how you want to do a task”

But what does this mean in practice? Let’s look at a quick real-life scenario. You are making lunch for a friend you are entertaining. They want a ham and cheese sandwich.

ham and cheese sandwich
Photo by petradr on Unsplash

Let’s see how they would interact with us in an imperative vs. a declarative style.

“declarative” defined as “politely asks for a sandwich” and “imperative” defined as “hands you a recipe”
Side note: An imperative guest is a rude guest

While a ridiculous example, it is indicative of the differences between the two approaches. In programming, you may be familiar with a common syntax that is declarative in nature, SQL. SQL lets us define what we want to retrieve, not the steps needed to retrieve it.

Let’s take a hypothetical SQL query and compare it to a hypothetical imperative implementation of it.

how declarative and imperative styles approach the same SQL query
SQL is a common example of declarative code.

The query above identifies the top ten sales reps in the U.S. by volume. As you can see, the declarative syntax of the SQL query communicates the same information in a much more succinct way.

For someone new who is reading our code, they get a good sense of what we are trying to accomplish with our query in one line. Using an imperative approach, they must take each step and piece together a mental model to understand what the code is trying to achieve. This is the power of declarative programming.

In JavaScript, you may be familiar with array methods like filter, map, and reduce that take the place of more imperative for loop implementations. If you want to see an example, this quick sixty-second video has an example.

A 60-second explainer on imperative vs. declarative

Before we continue, let’s take a quick look at Ramda.

So What Is Ramda?

Ramda is one of the most well-known functional programming libraries in JavaScript. Originally a fork of Lodash, this library has a ton of useful functions that allow us to write more readable and elegant code. The library also has useful features like auto-currying, immutability, and data-last functions. Taken together, these give developers a ton of flexibility when writing code. While many of the concepts we explore in this tutorial can be leveraged in plain JavaScript, Ramda may give you an easier interface to accomplish these tasks.

Of note, we will look at its pipe and compose functions in this tutorial. This is an implementation of a functional programming concept that we could implement in plain JavaScript, but we won't. We will instead use Ramda’s implementation in our example to learn. I definitely recommend that people spend time looking at Ramda. Here is an example of the terse code that you can write by leveraging and combining the library’s many functions and some of the concepts we will touch upon.

We see a number of concepts we will touch upon in this code.

The code above does the following:

  • It evaluates an array of API responses to see if any response returned an error.
  • If an error is found, we return a custom error object with a predefined message of “One or more API calls failed.”
  • If no error is found, we return our original array of API responses to use in our next step.

As you can see, Ramda can help us write cleaner, more descriptive code. It is definitely worth exploring.

Now that we have covered Ramda at a high level, let’s talk about some concepts that allow us to build more declarative code.

The Building Blocks of Declarative Code

heap of brightly colored plastic building blocks
Photo by Xavi Cabrera on Unsplash

Let’s now look at some concepts to help us write more declarative code. We can utilize a lot of these concepts in plain JavaScript, but using Ramda may give us some added benefits. As a result, I will note where Ramda may give us some added advantage. Let’s begin.

JavaScript functions are first-class citizens

JavaScript functions being first-class citizens means that functions are treated like any other type in JavaScript. Just like a string, you can:

  • Assign a function’s definition to a variable
  • Pass in a function as an argument into another function
  • Return a function definition as the result of a function call

This allows us to not only write higher-order functions. We can use this to give us flexibility in combining and extending functions. This enables us to also compose functions together and use a point-free programming style.

Write small functions and extract logic checks

This is one of the easier ways to make your code more readable. Instead of larger imperative blocks, look to extract each operation and condition-check into separate named variables. This is a good beginner's step towards function composition. Here is an example.

Note: We can further improve this code through ternaries, but just by extracting logic to variables, we already gain a great deal of readability.

Example of extracting logic into variables for readability

Avoid returning null and undefined whenever possible!

Null exceptions are a super common bug found in software. The inventor of the null type even apologized for it, estimating that it may have caused companies up to a billion dollars in pain over the years. Avoid returning these values since they can lead to unexpected errors in your code, and consider what other alternative data type makes sense for your function to return.

I recommend instead to return a value of the same type, like an empty array or empty string. It is a good practice to test to see if your function can handle null and undefined gracefully. Libraries like Lodash and Ramda have many functions that can help you avoid returning null orundefinedaccidentally when accessing a variable as well.

Point-free programming

Point-free or tacit programming style is a style where the arguments of a function are omitted when defining or passing the function. This is because a higher-level abstraction consistently invokes a passed function in a standard manner. This makes defining the arguments redundant. Let’s look at an example of point-free definition vs. other implementations.

Point-free JavaScript in action

As you can see, this leads to more readable code and is easier for the developer, too. No longer must we agonize about naming our argument to properly convey the data we are operating on! In plain JavaScript, you can start using this pattern in higher-level abstractions like map, filter, reduce, then, and catch.

We should note that using a functional library like Ramda allows us to significantly start introducing more point-free code into our application since many of its functions work like these higher abstractions. Take a look again at our Ramda example above.

Currying and partial application

Currying is the practice of transforming a function that takes many arguments to one that only takes one argument. We can accomplish this by partially applying one passed argument at a time and returning a function for the remaining arguments. This can be used to help reuse and extend our code. It also helps us build abstractions that make our code easier to read and use. Here is an example of using currying to build abstractions.

Currying allows us to abstract away logic in our application, making it easier for other devs to use.

While this may seem like a lot of work upfront, you achieve a ton of gains in readability and reusability. In the end, we are left with a declarative function that only does one very specific thing and only needs one passed argument to work. This allows other devs on your team to easily use your existing functions or extend functionality by partially applying new arguments.

Ramda has auto-currying by default, but it also allows you to pass the arguments in whatever order you like. For example, if a function takes three arguments, you can pass one to three arguments. Ramda also contains a curry function that converts any plain JavaScript function to a curried version of itself.

Piping and composing

Note: What follows is a simplistic explanation. There are some mathematical principles underpinning functional programming. If you’re interested, I recommend checking out this free book.

At its most basic, piping and its sibling compose are used in functional programming to chain functions together end to end. The result of one function is passed as an argument to the next function in the chain. These functions are defined in a point-free style. Piping and composing are not built into JavaScript, though there is a stage-one proposal at the moment for a pipe operator. We can use pipe or compose by either building it ourselves or by using Ramda. Below is an example of how pipe and compose work using the Ramda variant:

Pipe and composing greatly increase readability.

As you can see, the pipe and compose functions are quite powerful. I personally gravitate towards pipe since I find it easier to follow. Using pipe and by composing smaller functions, we can replace a lot of our imperative code bloat, like if-else blocks and array iterators.

Last Thoughts

Readable code begets functional code!

child looking at a book showing a large letter E on an orange background
Photo by Paul Hanaoka on Unsplash

Hopefully, you have left with a better sense of some concepts you can explore to build more declarative code. I definitely recommend taking a look at the Ramda library and playing with the concepts we went over in this tutorial. Additionally, this can be a gateway to functional programming and its many benefits.

Lastly, thank you for reading. I am thinking of doing a follow-up exploring how to use Ramda to effectively compose functions. If interested, please reach out and let me know!

Jayson Alzate
Jayson Alzate

Written by Jayson Alzate

Full-stack engineer working primarily in Javascript. Bootcamp grad with data analytics background and an MBA. Trying to get better every day!

Write a response

I should contest this article's points, in a little more polite way than Jason Knight did, although I agree with him.
I will start with a simple question: what is wrong with *IF*? What did it do to people who preach functional structures utterly…