Using Node.js to Code in a Functional Style

Functional coding just makes sense. It’s not only fun, but it expedites your developing process. Coding in a more functional style improves code reliability, and simplifies the debugging and testing process. Thanks to composition possibilities, it’s possible to divide all transformations into separate functions, give them meaningful names, think over the signature, and provide proper coverage with testing. You’ll be surprised how much cleaner your code will become. Most importantly, functional coding presents new approaches to solving old problems.

 

 

Agreeing to Code Practically

 

Getting every developer on the team to agree on a different approach to coding will be challenge. Some will be hesitant, others may reject the idea. It will take time to get familiarized with new libraries, make changes in code-style and rules of the static code analyzer, and write several “how-to” wiki pages. The documentation is academic and laborious. Well, seriously, look at the function signature `converge` from `ramda`:

 

(x1 → x2 → … → z) → [(a → b → … → x1), (a → b → … → x2), …] → (a → b → … → z)

 

In addition, development speed may decrease in the beginning. Most developers will take a couple of seconds to figure out the code below:

 

lines.filter(line => line.trim().length > 0)

 

…and way more time spent figuring out this:

 

filter(o(lt(0), prop('length'), trim))(lines)

 

It’s up to a team to figure out whether or not they want to take this new approach.

 

 

Using Familiar Tools

 

Lodash is used everywhere, but how many developers know that it has functional realization? Same functions but with the support for currying and composition. Let’s rewrite the following code:

 

const items = [{ price: 20 }, { price: 30 }, { price: 40 }]
const discount = 0.9
const priceWithDiscount = n => n * discount
const itemsTotalPrice = sum(map(map(items, 'price'), priceWithDiscount))

 

using `lodash-fp`:

 

const items = [{ price: 20 }, { price: 30 }, { price: 40 }]
const discount = 0.9
const getPriceWithDiscount = compose(multiply(discount), prop('price'))
const itemsTotalPrice = sumBy(getPriceWithDiscount(discount), items)

 

Let’s transform this code into a function and see how we can use it:

 

const { compose, multiply, prop, sumBy, map, pipe, pluck, filter, lt } = require('lodash/fp')

const users = [
  { name: 'Bob',
    cart: [{ name: 'Node.js book', price: 15 }, { name: 'Laptop', price: 500 }] },
  { name: 'John',
    cart: [{ name: 'Ruby book', price: 18 }] },
]

const getPriceWithDiscount = discount => compose(multiply(discount), prop('price'))
const getItemsTotalPrice = ({ discount }) => sumBy(getPriceWithDiscount(discount))

// we will derive the sum of all the user carts, with total more than 100
pipe(
  pluck('cart'),
  map(getItemsTotalPrice({ discount: 0.9 })),
  filter(lt(100)),
)(users)

 

Currying and composition allowed us to integrate calculations into the chain of receiving and filtering data without problems. It should also be noted that the code has become much cleaner.

 

At the moment, many libraries for javascript implement similar functionality. Personally, I prefer Ramda due to null-values processing and convenient functions for working with Promise (`pipeP` и `composeP`):

 

function getUserWithTweetsById(id) {
  return getUserById(id)
    .then((user) => {
      return fetchTweetsByUsername(user.twitter)
    })
}

const getUserWithTweetsById = pipeP(
  getUserById,
  prop('twitter'),
  fetchTweetsByUsername
)

 

In addition, ramda has an implementation Fantasy Land.

 

 

Introducing Fantasy Land

 

Fantasy Land is a formal specification of “algebraic structures” in JavaScript: monads, monoids, setoids, functors, chains and other. It describes sets of values, functions, and rules that these structures must follow. In other words, this specification defines the hierarchy of interfaces for algebraic structures.

 

FantasyLand-min

 

A developer would use algebraic structures for a higher level of abstraction and consistent interface. We need to write code asynchronous, with lazy evaluation and error handling — and for each of these cases there is a structure in Fantasy Land. Although, it’s up to you to decide if you’ll use these structures in your everyday work.

 

My advice for you is to read a great book to get acquainted with algebraic structures in Javascript – Professor Frisby’s Mostly Adequate Guide to Functional Programming – and it just might change your mind.

 

 

Lazy Evaluation

 

Lazy evaluation is natural for functional programming, and to some extent, you will get them simply by using `ramda`:

 

const { compose, map, add, take, into } = require('ramda')

const numbers = [1, 2, 3, 4]
const transducer = compose(map(add(1)), take(2))
into([], transducer, numbers) // => [2, 3]

 

This particular example `add(1)` will be executed only 2 times. Why? Thanks to transducers, composable, and efficient transformation functions.
Transducers allow transformation chair execution while traversing the collection, thus avoiding the creation of intermediate collections:

 

[1, 2, 3]
  .map(x => x + 1)
  .filter(x => x % 2 === 0)

 

First, it executes the cycle of 3 iterations for mapping, then the cycle of 3 iterations for filtering. For large collections and more complex transformations, you’ll want to rewrite this code using `reduce`:

 

[1, 2, 3].reduce((acc, x) => {
  if ((x + 1) % 2 === 0) acc.push(x)
  return acc
}, [])

 

Great, it only takes one cycle. The following code is equivalent:

 

const transducer = compose(
  map(x => x + 1),
  filter(x => x % 2 === 0),
)
into([], transducer, [1, 2, 3])

 

Let’s implement a filter and map using reducer in order to demonstrate it:

 

const append = (acc, x) => { acc.push(x); return acc }
const mapReducer = f => next => (acc, x) => next(acc, f(x))
const filterReducer = f => next => (acc, x) => (f(x) ? next(acc, x) : acc)

[1, 2, 3]
  .reduce(mapReducer(x => x + 1)(append), [])          // => [2, 3, 4]
  .reduce(filterReducer(x => x % 2 === 0)(append), []) // => [2, 4]

 

Still 2 cycles. However, since `append` has reducer interface `(acc, x) -> acc`, we can compose our reducers and perform mapping and filtering in a single pass:

 

const reducer = mapReducer(x => x + 1)(filterReducer(x => x % 2 === 0)(append))
[1, 2, 3].reduce(reducer, []) // => [2, 4]

//now to implement the `transduce` function and use `compose` from ramda instead of manual composition.
const transduce = (transducer, reducing, initial, list) => list.reduce(transducer(reducing), initial)
const transducer = compose(
  mapReducer(x => x + 1),
  filterReducer(x => x % 2 === 0),
)
transduce(transducer, append, [], [1, 2, 3]) // => [2, 4]

 

The transducer solution gives us cleaner syntax, more flexibility in changing transformations chain, and the ability to reuse the code (almost all `List` functions from ramda can be used as a transducer and run lazy).

 

And more complicated example in order to demonstrate the work with `transduce.js` streams:

 

// Accepts data in the format access.log in stdin,
// filters requests from localhost and outputs the result in stdout.
// Example:
//
//   stdin
//
//     127.0.0.1 - - [05/Sep/2017 13:18:22] “GET / HTTP/1.1” 200 -
//     66.249.70.18 - - [05/Sep/2017 13:19:13] “GET /hey HTTP/1.1” 200 -
//
//   stdout
//
//     [05/Sep/2017 13:19:13] “GET /hey HTTP/1.1” 200 -

const { compose, filter, map, test, match, not } = require('ramda')
const lines = require('transduce/string/lines')
const stream = require('transduce-stream')

const isLocal = test(/^127\.0\.0\.1/)
const isExternal = compose(not, isLocal)
const stripAddress = compose(head, match(/\[(.*)$/))

const parse = compose(
  lines(),
  filter(isExternal),
  map(stripAddress),
)

process.stdin.pipe(stream(parse)).pipe(process.stdout)
process.stdin.resume()

 

In this particular case, `parse` is the transducer, and all magic is hidden in `transduce-stream`. All that is left to do is implement transformations’ logic. In addition to simple pipeline, we can use `parse` with other libraries working with transducers (for example RxJS or Highland).

 

 

Getting Acquainted with Reactive Programming

 

Let’s complicate the previous task to demonstrate the idea. Now we need to parse the logs from several files, filter them, and output to `stdout`. To solve the task, we will use the library Highland that can be found on Github:

 

const H = require('highland')
const fs = require('fs')
const parse = require('./parse')

const filenames = ['access.log', 'access.log.0', 'access.log.1', 'access.log.2']

H(filenames)
  .map(H.wrapCallback(fs.readFile))
  .transduce(parse)
  .pipe(process.stdout)

 

The code has not changed much, we have just changed the way we extract the data. It should be easy enough to understand what happens here without access to the documentation of `Highland`: we read the files, merge them into one stream, transform them with transducer help, and output them to stdout. Highland provides us with the ability to manipulate the streams as collections, so that we can easily use our `parse` function here.

 

Reactive programming is a programming with asynchronous data stream — any data, whether it is a server response, database query result, or UI events. This is where the “functional magic” appears. For example, data from several merged streams can be filtered and turned into new stream containing only the necessary data. All this is done with quite familiar merge, map, filter, transduce and so on.

 

 

Conclusion

 

Many elements of functional programming can be included in your everyday work, and you don’t need to retrain or change the stack. If your development team is up for the challenge, give functional programming a shot.

 

Previous

Next