Functional Programming Patterns

Yann Gouffon — June 25th, 2019

Few months ago, Kyle Shevlin did a great course on egghead, followed by a series of articles, about some basic concepts of functional programming in JavaScript. As an adept of this very popular paradigm, I found that even very basics, I'm not using all of them on a daily basis. So, here is a quick recap of how cool those patterns are and how they can help you improve your functional code.

High order function

“The high order function (HOF) is a function that accepts a function as an argument and returns a new function 😅” Just read the example, it will directly make more sense to you.

1// Our high order function 2const withCountLog = fn => { 3 let count = 0; 4 5 // Returns a new function 6 return (...args) => { 7 console.log(`Called ${++count} times`); 8 return fn(...args); 9 } 10} 11 12const add = (x, y) => x + y; 13const countedAdd = withCountLog(add); // binding 14 15countedAdd(52, 4); // Called 1 times 16countedAdd(63, 5); // Called 2 times 17countedAdd(74, 6); // Called 3 times

Curried function

“A curried function is a higher-order function that returns a series of functions each accepting only one argument and only evaluating once we receive our final argument.”

1const add = x => y => z => x + y + z; 2console.log(add(1)(2)(3)); // 6

OK this example makes no sense at all. Now, if you start using it for splitting your logic into something simpler and more maintainable, it starts to make a lot of sense:

1// Our curried function 2const getFromAPI = baseURL => endpoint => cb => 3 fetch(`${baseURL}${endpoint}`) 4 .then(res => res.json()) 5 .then(data => cb(data)) 6 .catch(err => new Error(err)); 7 8// Main level 9const getGithub = getFromAPI('https://api.github.com'); 10 11// Sub levels 12const getGithubUsers = getGithub('/users'); 13const getGithubRepos = getGithub('/repositories'); 14 15getGithubUsers(data => console.log(data.map(user => user.login)));

Finally, the order of arguments are very important. Here we have baseURL => endpoint => cb for the reason illustrated above. Always remind yourself this simple rule: the order is from most specific to least specific argument.

Composition

This one can have its own article or even a series of articles, but I'm sure there are a lot of great resources out there. Basically, it's a function used to compose other functions in a specific order to return to desired output .

1// Our composition helper 2const compose = (...fns) => x => 3 fns.reduce((acc, fn) => fn(acc), x); 4 5const lower = str => str.toLowerCase(); 6const sanitize = str => str.replace(/[^a-z0-9 -]/g, ''); 7const clean = str => str.replace(/\s+/gm, '-'); 8 9const slugify = compose( 10 lower, 11 sanitize, 12 clean, 13); 14 15console.log(slugify('I love $$$ noodles')); // i-love-noodles

This way is much cleaner and more readable than something like clean(sanitize(lower('My string')));, especially with more complex structure. The order is also very important; in this example, put sanitize before lower and your I will simply disappear.

You can also use the kind-of .map approach:

1// Our refactored composition helper 2const compose = x => ({ 3 map: f => compose(f(x)), 4 end: () => x, 5}); 6 7const lower = str => str.toLowerCase(); 8const sanitize = str => str.replace(/[^a-z0-9 -]/g, ''); 9const clean = str => str.replace(/\s+/gm, '-'); 10 11const slugify = str => compose(str) 12 .map(lower) 13 .map(sanitize) 14 .map(clean) 15 .end(); 16 17console.log(slugify('I love $$$ noodles')); // i-love-noodles

Conclusion

As I said, simple concepts, but very helpful to produce any kind of functional code. It will also help you to maintain consistency across your code base if you choose this great paradigm.


Supported with 💛 by Antistatique