Why Be Functional?

1
2
3
4
5
6
7
8
// Plain JS
let incompleteTasks = tasks.filter(function(task) {
return !task.complete;
});

// Functional
let isCompleteTask = R.whereEq({complete: false}); //=> Fun: Object -> Bool
let giveMeIncompleteTasks = R.filter(isCompleteTask); //=> Fun: Object -> Object
  • Self-document
  • Testable
  • Short
  • Compositional

Basis

Meeting Particial Apply & Curry

Particial Apply - Specifies some arguments up front, producing a function that’s waiting for all the rest arguments.

1
2
3
4
5
6
7
let greet = (salutation, title, firstName, lastName) =>
salutation + ', ' + title + ' ' + firstName + ' ' + lastName + '!';

let sayHello = partial(greet, ['Hello']);
let sayHelloToMs = partial(sayHello, ['Ms.']);

sayHelloToMs('Jane', 'Jones'); //=> 'Hello, Ms. Jane Jones!'

Curry - Call a function with incomplete arguments, it returns a function which takes the remaning arguments

1
2
let add = (x, y) => x + y;
let curriedAdd = x => y => x + y;

But what if I have lots of parameters?

1
2
3
4
let crazyFun = (a, b, c, d, e) => ...;
/* curried way?
crazyFun(1)(2)(3)(4)(5);
*/

We can do:

1
2
3
4
5
6
7
crazyFun(1, 2, 3, 4, 5);
// of
crazyFun(1, 2)(3, 4, 5);
// or
crazyFun(__, 2)(1)(3, __, 5)(4);
// or
crazyFun(__, 2)(1, __, 4)(3, 5)

Example

###Function Factory - Compose

Compose - The reverse of pipe

1
2
3
4
5
6
7
8
9
let toUpperCase = function(x) {
return x.toUpperCase();
};
let exclaim = function(x) {
return x + '!';
};
let shout = compose(exclaim, toUpperCase);

shout("I love mondo"); //=> "I LOVE MONDO!"

You can implement a compose by many ways:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function composeTwo(f, g) {
return (...args) => f(g(...args));
}

function compose(...fns) {
return function composed(result) {
let list = fns.slice();

while (list.length > 0) {
result = list.pop()(result);
}

return result;
}
}

Pure Function

Pure function - A function that, given the same input, will always return the same output and does not have any observable side effect.

Think about slice and splice: the latter change the origin array, it’s impure.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var xs = [1, 2, 3, 4, 5];

// pure
xs.slice(0, 3);
//=> [1, 2, 3]

xs.slice(0, 3);
//=> [1, 2, 3]

xs.slice(0, 3);
//=> [1, 2, 3]


// impure
xs.splice(0, 3);
//=> [1, 2, 3]

xs.splice(0, 3);
//=> [4, 5]

xs.splice(0, 3);
//=> []

Side effects may include, but are not limited to

  • changing the file system
  • inserting a record into a database
  • making an http call
  • mutations
  • printing to the screen / logging
  • obtaining user input
  • querying the DOM
  • accessing system state - free variables, etc.

A pure function can’t produce any observable effect.

Immutability

  • Don’t ever make a change to value.
  • Always create a new copy.
  • Helpful for reducing side effects.

We can use lenses to “make change”:

1
2
3
4
5
6
let xLens = R.lens(R.prop('x'), R.assoc('x'));
//----------------- ↑getter ------ ↑setter ---

R.view(xLens, {x: 1, y: 2}); //=> 1
R.set(xLens, 4, {x: 1, y: 2}); //=> {x: 4, y: 2}
R.over(xLens, R.negate, {x: 1, y: 2}); //=> {x: -1, y: 2}

Reference

«Mostly Adequate Guide»

Why Ramda

Favoring Curry

«Thinking in Ramda»

«Functional Light JS»

Ramda.js