Enrich your Javascript with simple functional programming techniques
Tags: javascript, currying, functional, refactoring
Published: 2022-08-18 08:27:00

Being a multi-paradigm programming language, Javascript allows writing code in different styles. And while you probably don't want to mix OOP and functional approaches on the architectural level, there is no reason to limit the power of a multi-paradigm language on a smaller code unit level. Nonetheless, functional programming techniques are often ignored where they would fit nicely. In this article, I'd like to demonstrate how one of such simple techniques can make our code better.

Without further theoretical reasoning, here is what we are going to do: 

- Write a module that checks if all object fields pass the provided validation rules.
- Start with the most straightforward imperative implementation and improve the code in a couple of steps.
- Compare the results.

We will not do this just to demonstrate how different techniques can be used to solve the same problem. Our goal is to improve two very important metrics of clean code: re-usability and readability. This article covers the very basics, but I will provide some links at the end if you want to dive a little deeper into this topic. 

### Just make it work first

Assume our application allows users to publish new articles that will show up somewhere in the feed. Just like the one you are reading now. The shape of the article will be as simple as possible:

```typescript
{
  title: String;
  tags: Array<String>;
  text: String;
}
```

On the server side before saving a new article in the database we have to validate if article fields conform to the following validation rules:

- `title` is a string not shorter than 20 and not longer than 200 characters.
- `tags` is an array of strings with at least one element in it.
- `text` is a string not shorter than 200 characters and not longer than 100 000 characters.
  
Translated to code, the first and most naive definition of validator rules might look like this:

```javascript
const rules = {
  title: value => typeof value === 'string' && value.length > 20 && value.length < 200,
  tags: value => Array.isArray(value) && value.length > 0,
  text: value => typeof value === 'string' && value.length > 200 && value < 100_000
}
```

(side note) Yes, you can use the `_` symbol as a [numeric separator](https://v8.dev/features/numeric-separators) in big numbers since ES2021 to improve readability. Just in case you don't do this yet:)

Now let's create the function that will iterate over the object's fields and validate each of those. Instead of just returning `true` or `false` to indicate if the object passed validation, we want the function to return an array of erroneous fields (if any) so we can provide some feedback to the client. 

```javascript
const getValidationErrors = (rules, objectToValidate) => {
  return Object.entries(rules).reduce((acc, [key, validatorFn]) => {
    const value = objectToValidate[key];
    if (!validatorFn(value)) {
      return [...acc, key];
    }
    return acc;
  }, []);
}
```

The validation algorithm is quite primitive:

- Create an array of key-value pairs of the `rules` object where the key is the name of the field we are validating.
- For each key-value pair get the relevant field's value and run the validator function provided for this field name in the `rules`. If validation fails, add the key name in question to the errors array.
- Return the errors array (return value of the `reduce` function).

We would want more from this algorithm in a real-world application(validate inputs, handle errors), but for this demo purpose let's keep the shortest version.

### Obvious refactoring

There are some drawbacks to this implementation that we can spot immediately. We will likely need to validate the same conditions in other places in our application. Extracting these validators into reusable functions exported from the validation utils module is an obvious improvement. 

```javascript
export const isString = (value) => {
  return typeof value === 'string' || value instanceof String;
}

export const isNonEmptyArray = (value) => {
  return Array.isArray(value) && value.length > 0;
}

export const lengthIsInRange = (value, min, max) => {
  return value.length > min && value.length < max;
}
```

No changes needed in the `getValidationErrors` function. But `rules` now look much better:

```javascript
const rules = {
  title: isString(value) && lengthIsInRange(value, 30, 250),
  tags: isNonEmptyArray(value),
  text: isString(value) && lengthIsInRange(value, 0, 100_000)
}
```

Let's look at the code we have written so far and evaluate it: 
- Our code does its job, fields are being validated.
- The `getValidationErrors` function is not too bad. It's concise and provides feedback. 
- Validators are defined as reusable functions, so our code is DRY enough. 

Are we good to push the changes and open a PR? :ok_hand: 

It's probably OK to do so at this point. But our code can be made better with very little effort. We are calling functions, but do not use good functional programming practices.


### Currying to make it elegant
If you look at the code you will likely notice how we repeatedly write `(value)` calling each validator and apply the logical `AND` operator multiple times. When you see this, chances are high that these repeating parts can be abstracted. 

In my validator rule, I'd like to just list validators separated by commas. Then inside `getValidationErrors` function, I will call each of these validator functions with the `value` to validate as a single parameter. It's easily doable for `isString` and `isNonEmptyArray`, but `lengthIsInRange` has two other parameters that we need to apply in advance before the function will be called along with other validators. This is where a simple concept of [currying](https://en.wikipedia.org/wiki/Currying) comes into play. Let's rewrite `lengthIsInRange` as a higher order function:


```javascript
export const lengthIsInRange = (min, max) => {
  return (value) => value.length > min && value.length < max;
}
```

Here we prepare our validator by fixing `min` and `max` arguments beforehand in the lexical context of the outer function. This allows us to call the actual validator function later with only one argument: the object to validate. Which is exactly what we needed.  

All we need to do the `getValidationErrors` function is to rename `validatorFn` to `validators` and change this line:

```javascript
if (!validatorFn(value))
```

to:

```javascript
if (!validators.every((rule) => rule(value)))
```

Here is the full version of the updated `getValidationErrors`:
 
```javascript
const getValidationErrors = (rules, objectToValidate) => {
  return Object.entries(rules).reduce((acc, [key, validators]) => {
    const value = objectToValidate[key];
    if (!validators.every((rule) => rule(value))) {
      return [...acc, key];
    }
  }, []);
}
```

And our rules definitions will now look like this:

```javascript
const rules = {
  title: [isString, lengthIsInRange(30, 250)],
  tags: [isNonEmptyArray],
  text: [isString, lengthIsInRange(200, 100_000)]
};
```

Compare this version to previous implementations. The goal we set at the beginning was to improve code re-usability and readability. I will leave it up to you to decide which version looks better from this point of view.


### Bonus: function as a language first-class object

We probably don't want to run any additional validations if the field is optional and the value for this field is missing. We hence need a way to distinguish between required and optional fields. Let's create a separate validator in its simplest possible version for this purpose:

```javascript
export const isDefined = (value) => {
  return value !== undefined;
}
```

And update our rules, making `tags` field optional:

```javascript
const rules = {
  title: [isDefined, isString, lengthIsInRange(30, 250)],
  tags: [isNonEmptyArray],
  text: [isDefined, isString, lengthIsInRange(200, 100_000)]
};
```

Now in our `getValidationErrors` function, we can check if `isDefined` is among validators first and only then run the remaining validations, otherwise just proceed to the next field. How do I do this? As you know, functions are [first class objects](https://en.wiktionary.org/wiki/first-class_object) in Javascript, so I can check for function presence in an Array just like I would do for a string or a number:

```javascript
if (!isDefined(value) && !validators.includes(isDefined)) {
  return acc;
}
```

Full version of revamped `getValidationErrors`:

```javascript
const getValidationErrors = (rules, objectToValidate) => {
  return Object.entries(rules).reduce((acc, [key, validators]) => {
    const value = objectToValidate[key];
    if (!isDefined(value) && !validators.includes(isDefined)) {
      return acc;
    }
    if (!validators.every((rule) => rule(value))) {
      return [...acc, key];
    }
  }, []);
}
```

If the value is not defined and `isDefined` is not among validators the function returns early. Since it happens in the `reduce` context, don't forget to return the unchanged accumulator value. This way, we keep our rules definitions clean, avoiding the need of creating data structures like `{required: true, validators: [...]}`. :clap:

Bear in mind this is not necessarily the way to go for every single task. You should always weigh the pros and cons and pick the right data structure for your requirements. But to make efficient choices it is paramount to be aware of the capabilities that language offers you. 

### Is `getValidationErrors` getting too messy?

It probably is. While the function body is still only 9 lines long and its readability is acceptable, the function is doing several things now. Since we are talking about functional programming techniques, let's turn `getValidationErrors` into a chain of operations applied to the `rules` object entries:

```javascript
const getValidationErrors = (rules, objectToValidate) => {
  return Object.entries(rules)
    .map(([field, validators]) => ({
      field,
      validators,
      value: objectToValidate[field]
    }))
    .filter(shouldReportValidationError)
    .map(({field}) => field);
}

const shouldReportValidationError = (validationData) => {
  return shouldBeValidated(validationData) && !passesAllValidators(validationData);
}

const shouldBeValidated = ({validators, value}) => {
  const canSkipValidation = !isDefined(value) && !validators.includes(isDefined);
  return !canSkipValidation;
};

const passesAllValidators = ({validators, value}) => {
  return validators.every((rule) => rule(value));
};
```

A quick look at the `getValidationErrors` is enough now to understand it is doing the following things for every field:
- Mapping the field's validation rules and value to a data structure for further processing.
- Applying validation rules abstracted in `shouldReportValidationError`.
- Mapping erroneous fields to the correct error format (just the name of the field in our case).

In a real-world scenario when the logic behind every step will likely be more complex, the value of such refactoring grows. We decomposed the logic in several steps and no longer mix array processing with imperative code on the `getValidationErrors` level.

### Code examples

You can play around with code examples from this article in an online editor by [this link](https://stackblitz.com/edit/node-6r5mbr?file=after.js). Just type `node <file name>.js` in the terminal to run the code. 

### Conclusion
Hope this article motivates you to leverage currying and other useful functional programming techniques in your Javascript code.

If you wish to learn more about functional techniques in Javascript I highly recommend [this series of articles](https://randycoulman.com/blog/categories/thinking-in-ramda/) by Randy Coulman. In particular, [the third article](https://randycoulman.com/blog/2016/06/07/thinking-in-ramda-partial-application/) in the series explains the idea of currying or partial application in more detail. 

Thanks for reading!