# Functional programming

Since the birth of the JavaScript language, it has the brand of functional programming. It treats functions as an independent data type, and is in a completely equal position with other data types. In the JavaScript language, you can use object-oriented programming or functional programming. Some people even say that JavaScript is the first functional programming language to be adopted on a large scale.

Various new features of ES6 make functional programming more convenient and powerful. This chapter introduces how ES6 performs functional programming.

## Currying

Currying refers to splitting a multi-parameter function into a series of functions, and each split function accepts only one parameter (unary).

```
function add(a, b) {
return a + b;
}
add(1, 1); // 2
```

In the above code, the function `add`

accepts two parameters `a`

and `b`

.

Currying is to split the above function into two functions, each of which accepts only one parameter.

```
function add(a) {
return function (b) {
return a + b;
};
}
// Or use arrow function writing
const add = (x) => (y) => x + y;
const f = add(1);
f(1); // 2
```

In the above code, the function `add`

only accepts one parameter `a`

and returns a function `f`

. The function `f`

also only accepts one parameter `b`

.

## Function synthesis

Function composition refers to combining multiple functions into one function.

```
const compose = (f) => (g) => (x) => f(g(x));
const f = compose((x) => x * 4)((x) => x + 3);
f(2); // 20
```

In the above code, `compose`

is a function synthesizer, used to synthesize two functions into one function.

It can be found that currying is closely related to function composition. The former is used to split a function into multiple functions, and the latter is used to merge multiple functions into one function.

## Parameter inversion

Parameter flip refers to changing the order of the first two parameters of a function.

```
var divide = (a, b) => a / b;
var flip = f.flip(divide);
flip(10, 5); // 0.5
flip(1, 10); // 10
var three = (a, b, c) => [a, b, c];
var flip = f.flip(three);
flip(1, 2, 3); // => [2, 1, 3]
```

In the above code, if you follow the normal parameter order, 10 divided by 5 equals 2. However, the new function obtained after the parameters are inverted, the result is 5 divided by 10, and the result is 0.5. If the original function has 3 parameters, only the positions of the first two parameters are reversed.

The code for parameter inversion is very simple.

```
let f = {};
f.flip =
(fn) =>
(a, b, ...args) =>
fn(b, a, ...args.reverse());
```

## Execution boundary

The execution boundary (until) refers to the function execution until the condition is met.

```
let condition = (x) => x > 100;
let inc = (x) => x + 1;
let until = f.until(condition, inc);
until(0); // 101
condition = (x) => x === 5;
until = f.until(condition, inc);
until(3); // 5
```

In the above code, the condition of the first paragraph is to execute until `x`

is greater than 100, so when the initial value of `x`

is 0, it will be executed until 101. The condition of the second paragraph is to execute until it is equal to 5, so the final value of `x`

is 5.

The implementation of the execution boundary is as follows.

```
let f = {};
f.until =
(condition, f) =>
(...args) => {
var r = f.apply(null, args);
return condition(r) ? r : f.until(condition, f)(r);
};
```

The key to the above code is to return the result if the condition is met, otherwise it will continue to execute recursively.

## Queue operation

Queue (list) operations include the following.

-`head`

: Remove the first non-empty member of the queue. -`last`

: Remove the last non-empty member of the limited queue. -`tail`

: Take out other non-empty members except "head of queue". -`init`

: Take out other non-empty members except "end of queue".

The following is an example.

```
f.head(5, 27, 3, 1); // 5
f.last(5, 27, 3, 1); // 1
f.tail(5, 27, 3, 1); // [27, 3, 1]
f.init(5, 27, 3, 1); // [5, 27, 3]
```

The implementation of these methods is as follows.

```
let f = {};
f.head = (...xs) => xs[0];
f.last = (...xs) => xs.slice(-1);
f.tail = (...xs) => Array.prototype.slice.call(xs, 1);
f.init = (...xs) => xs.slice(0, -1);
```

## Merge operation

The merge operation is divided into two types: `concat`

and `concatMap`

. The former is to combine multiple arrays into one, and the latter is to process the parameters first, and then combine the processing results into an array.

```
f.concat([5], [27], [3]); // [5, 27, 3]
f.concatMap((x) => "hi " + x, 1, [[2]], 3); // ['hi 1','hi 2','hi 3']
```

The implementation code of these two methods is as follows.

```
let f = {};
f.concat = (...xs) => xs.reduce((a, b) => a.concat(b));
f.concatMap = (f, ...xs) => f.concat(xs.map(f));
```

## Pairing operation

The pairing operation is divided into two methods: `zip`

and `zipWith`

. The `zip`

operation pairs the members of the two queues one by one to form a new queue. If the two queues are not of equal length, the extra members of the longer queue will be ignored. The first parameter of the `zipWith`

operation is a function, and then the following queue members will be paired one by one, input the function, and the return value will form a new queue.

The following is an example.

```
let a = [0, 1, 2];
let b = [3, 4, 5];
let c = [6, 7, 8];
f.zip(a, b); // [[0, 3], [1, 4], [2, 5]]
f.zipWith((a, b) => a + b, a, b, c); // [9, 12, 15]
```

In the above code, the first parameter of the `zipWith`

method is a summation function, which adds up the members of the following three queues one by one.

The implementation of these two methods is as follows.

```
let f = {};
f.zip = (...xs) => {
let r = [];
let nple = [];
let length = Math.min.apply(
null,
xs.map((x) => x.length)
);
for (var i = 0; i < length; i++) {
xs.forEach((x) => nple.push(x[i]));
r.push(nple);
nple = [];
}
return r;
};
f.zipWith = (op, ...xs) => f.zip.apply(null, xs).map((x) => x.reduce(op));
```

## Reference link

- Mateo Gianolio, Haskell in ES6: Part 1