Functional coding just makes sense. It’s not only fun, but it expedites your developing process. Node programming is a more functional style of coding that 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. Read on to learn more about javascript functional programming in Node.js. You can always contact us if you have any questions about our software development services.
Node Programming: Agreeing to Code Practically
Getting every developer on the team to agree on a different approach to coding will be challenging. Some will be hesitant, others may reject the idea. It will take time to get familiarized with new JS 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 node 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 node functional programming approach. Learn more about the functional programming language, Elixir.
Using Familiar Tools for Node Function
Lodash is used everywhere, but how many developers know that it has functional realization? The JS functional programming library and Lodash are a powerful duo. Lodash supports the 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 functional programming 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 for JS Functional Programming
Fantasy Land is a formal specification of “algebraic structures” in JavaScript, or JS functional programming: monads, monoids, setoids, functors, chains and others. It describes sets of values, node functions, and rules that these structures must follow. In other words, this specification defines the hierarchy of interfaces for algebraic structures.
A developer would use algebraic structures for a higher level of abstraction and consistent interface. We need to write code asynchronously, 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 in Javascript Functional Programming
Lazy evaluation is natural for JS 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 javascript functional programming 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 the 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 the 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 Node 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 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 a new stream containing only the necessary data. All this is done with quite a familiar merge, map, filter, transduce and so on.
Conclusion to Functional Programming in Nodejs
Many elements of JS 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 Node.js functional programming and development a shot. Contact our experienced team today if you have any questions about Node.js programming or if you would like to set up a consultation.