A beginner friendly intro to functional programming Pt2

Johann Schuster
codeburst
Published in
7 min readNov 7, 2017

--

Photo by Dayne Topkin on Unsplash

This article is part two of “A beginner friendly intro to functional programming”, that establishes why we would consider incorporating functional programming techniques into our code, key traits of the styles, what higher order functions, anonymous functions and recursive functions are. If you’d like to check that out here’s a link.

The goal of this article is to build on the foundations of the previous one, and to develop our understanding of the ways functions can be combined to create powerful, easy to read and maintain code. By the end, we would hopefully have a clear understanding of applying the powerful methods of partial application, curried functions and function composition to our FP style.

These concepts can get pretty thorny quickly, and my main goal is to keep things simple, reviewing how functions run will help us easily understand the following concepts.

Functions in JavaScript can take in any amount of parameters, the amount of parameters included in a function’s declaration determines the function’s arity, which refers to the amount of parameters defined when the function was declared. Because JavaScript allows us to call a function with any number of arguments, we can call any function with less or more arguments than it expects. If we were to check the arity of a function with one parameter, and pass in more than one argument, it will return one. If were to pass in two arguments to a function that expects three, it will still return 3 as its arity length.

function unary(a){}
function binary(a,b){}
function ternary(a,b,c){}
console.log(unary.length) // logs: 1
console.log(binary.length) // logs: 2
console.log(ternary.length) // logs: 3

When a function’s parameters all have values, the function can be applied over its arguments, which is just a function being run. We would say that this is total application of a function, or that the function is fully being fully applied, because it received as many arguments as it expected.

In JavaScript, every time we run a function, total application happens, even if we didn’t supply the correct amount of arguments. When we supply a function with arguments, they are bound to variables in the function’s lexical scope. When we don’t pass a value to a parameter it gets bound to that function’s scope as undefined, and we know what result that brings. While this approach works, it limits us. Remember that our goal is to write reusable code that’s easy to reuse, test and debug.

Partial Application

Partial application is an intelligent way of writing a function so that if we pass in 2 arguments to a function with an arity of 3 (expects 3 arguments) the supplied arguments will get bound to the appropriate values in the function’s scope, and then wait until the final argument is passed in to run the function. In partial application, a function that did not have all it’s parameters bound, will return another function with a new, lower arity function, which will take in less arguments because some have already been partially applied.

EXAMPLE OF FULL APPLICATION

function mapFunc(func, arr){
return arr.map(func);
}
//es6 implementation of above function
//const mapFunc = (func, arr) => arr.map(func);
const double = (num) => num * 2;
const triple = (num) => num * 3;
console.log(mapFunc(double, [ 7, 3, 9] )); // logs: [14, 6, 18]
console.log(mapFunc(triple, [ 7, 3, 9] )); // logs: [21, 9, 27]

mapFunc has an arity of 2, we supplied two arguments. The function was fully applied to its arguments.

If we call mapFunc with one argument:

console.log(mapFunc(double)); // “error”

EXAMPLE OF A PARTIALLY APPLIED FUNCTION

function partialFunc(func){
return function(arr) {
return mapFunc(func, arr);
}
}
// es6 implementation of above function
//const partialFunc = (func) => arr => mapFunc(func, arr);
const doubleItems = partialFunc(num => num * 2);
const tripleItems = partialFunc(num => num * 3);
console.log(doubleItems([ 7, 3, 5 ])); // logs: [14, 6, 18]
console.log(tripleItems([ 7, 3, 5 ])); // logs: [21, 9, 27]

If we call our partially applied function with one argument:

console.log(partialFunc(double)); 
// logs: (func) => arr => mapFunc(func, arr)

Currying

Currying is a form of partial application. When one calls a curried function without all of its arguments, it returns another function waiting for the next.

Simple Curried function
function curried(a) {
return function(b) {
return function(c) {
return a + b + c;
}
}
}
console.log(curried(2)(4)(6)) // logs: 12

The curried function is also applied partially, and we could apply each of its functions one at a time until all the parameters have been passed in.

const a = curried(2)
const b = a(4)
const c = b(6)
console.log(c); // logs: 12

This works great, but if we wanted to have a function that does something else, like multiply, we would have to write another multiply curried function . We can write a generic curried function that we can pass a function like add or multiply to, we’d be able to reduce our code and use it in many different ways.

// Simple pure functions that perform an operationfunction add(a,b,c) { return a+b+c; }
function multiply(a,b,c) { return a*b*c; }
// Generic curry functionfunction curry(fn) {
return function curried() {
var args = [].slice.call(arguments);
return args.length >= fn.length ?
fn.apply(null, args) :
function () {
var rest = [].slice.call(arguments);
return curried.apply(null, args.concat(rest));
};
};
}
var curriedAdd = curry(add);
var curryMultiply = curry(multiply)
console.log(curriedAdd(4)(5)(6)); // logs: 15
console.log(curryMultiply(7)(8)(9)) // logs: 504
console.log(curriedAdd(4,5)(6)) // logs: 15
console.log(curryMultiply(7,8,9)) // logs: 504
// es6 implementation of our generic function
function curry(fn) {
return function curried(…args) {
return args.length >= fn.length ?
fn.call(this, …args) : (…rest) => {
return curried.call(this, …args, …rest);
};
};
}

Now instead of having one large function with a single purpose, we have a generic one that takes in little functions with definite tasks, applies them to a final input.

Simply put, what this function does is:

  • return a function
  • store the arguments we passed to it
  • check for the arity of the function
  • if we passed in all the arguments we’re done
  • if not return a function keeping in mind of all the arguments already passed in, with a lower arity.

I highly encourage spending some more time learning about currying and partial application, there are many ways to build upon this, seeing that our goal here is to build a perspective of functional programming paradigm, we will move on:) At the end I will include links to some cool resources.

Function Composition

Function composition is all about combining little pieces of logic together that make up more complex functions. The idea is to break up a complex task into pieces of testable pure functions.

We can think of composing functions by linking them together, and the result of one function becomes the input for the next. The result of the final function is the overall result.

Composed functions evaluate from the inside out, if we have simple composed math function it would look like: f ( g ( x )) and the evaluation would begin with x, then g and the f. We can also write a function to evaluate from left to right which is called a pipeline. The following is an example of a simple pipeline function, if we wanted to inverse the order of evaluation we would use the Array.prototype.reduceRight() javascript method instead of Array.prototype.reduce().

Simple example of pipeline composition (using es6 for brevity).

// add 5
const add5 = (num) => num + 5;
// triple
const triple = (num) => num * 3;
// add note
const note = (str) => `Your lucky number: ${str}`;
// apply
const apply = (fn1, fn2) => fn2(fn1);
//compose
const compose = (arr) => (val) => arr.reduce(apply, val);
const add5AndTriple = compose([add5, triple, note]);
console.log(add5AndTriple(4));
// logs: “Your lucky number is: 27”
// reusing the functions are easy
const demoFunc = compose([add5, triple, add5, triple, note]);
console.log(demoFunc(4));
// Logs: “Your lucky number is: 96”

Simply put, we have three functions, add, triple and note, each focusing on one single task. Our apply function is a predicate that we will use when we make use of the reduce method (remember that the reduce method takes in a function as its first argument). Our compose function takes in an array, and reduces it with the apply function. We partially apply the compose function to a variable which, when called, takes in the value we want to have some transformation done to.

Given that this is just a basic demonstration, this method becomes very useful when dealing with data that needs operating on. If we had a set of data that needed to filtered, organized and modified, we can easily break up our code into simple testable pure functions, compose them together and export the single function to reuse in our code.

In the interest of keeping these articles brief and convenient to read, there will be one more to top off this introduction to functional programming that will discuss a few more key and important concepts and break down some of the nifty JavaScript methods that adds value to our functional programming toolkit.

Check out these awesome resources for learning more about the above mentioned concepts.

Photo by Daniel Cheung on Unsplash

I really hope this article was helpful to you. If you found some value in it, please give it a little clap, and as always, feel free to comment, share or reach out!

--

--