Let me give you a simple example of functional programming

An implementation of addition and multiplication procedures: 2* (4+0) + 4*2

Using common imperative programming ideas:

var a = 4 + 0;
var b = 2 * a;
var c = 4 * 2;

var result = b + c // 16
Copy the code

Using functional programming ideas:

var add = function(x, y) { return x + y }; var multiply = function(x, y) { return x * y }; var a = 4; var b = 2; var c = 0; var result = add(multiply(b,add(a, c)), multiply(a, b)); / / 16Copy the code

In contrast, imperative programming is a way of telling “so-and-so to do something”, whereas functional programming is telling the computer “how to do something” and then calling it when someone wants to do it, without specifying who should do it. If you look closely, you will see that there are several laws of arithmetic in mathematics:

/ / associative law + z = x + (x + y) (y + z) add (add (x, y), z) = = add (x, the add (y, z)); X +y = y+x add(x, y) == add(y, x); Add (x, 0) == x; Multiply (x, y) == multiply(x, y), multiply(x, z));Copy the code

But the nesting of the calling functions that get result at the end is a bit confusing. And then we convert it according to these mathematical rules

Multiply (b,add(a, c)), multiply(a, b)); Add (a, c) == a) add(multiply(b, a), multiply(a, b)); Multiply (b, add(a, a)); multiply(b, add(a, a));Copy the code

In fact, functional programming is mathematics, but it just happens to be applied to programming. It is derived from the “category theory” in mathematics, which holds that all conceptual systems in the world can be abstracted into “categories”. A set of all the members of a relational deformation is a category, simply understood as “set + function”. Just like y = f(x) in mathematics, x is derived from the corresponding y by the deformation relation f function.

Here are a few things about functional programming.

1. The function is “first-class citizen”

A “first class citizen” is a function that, like any other data type, has equal status. It can be assigned to other variables, passed in as arguments to another function, or returned as a value from another function.

For example, the print variable in the following code is a function that can be used as an argument to another function.

var print = function(i){ console.log(i); }; [1, 2, 3]. The forEach (print);Copy the code

2. Pure functions

If a function depends on the external environment, its behavior becomes unpredictable. Because you don’t know when the external environment will be changed, there seems to be an old saying that speaks directly to the problems side effects can cause code organizations: shareable and modifiable variables are the root of all evil.

A pure function is one in which the same input always returns the same output without any observable side effects. If you pass the same input parameter to a function, it will only output the same return. Its output will not be affected by external variables or the execution environment. For example:

Whether external variables can affect the output

Var fn1 = (STR) => {console.log(STR + '' + tips)} fn1('hello') // Hello JavaScript tips Var fn2 = (STR) => {var tips = 'JavaScript' console.log(STR + '' + tips)} fn1('hello') // hello JavaScriptCopy the code

Both slice and splice can be used to intercept arrays

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] // not pure xs.splice(0,3); / / = > [1, 2, 3] xs. The splice (0, 3); / / = > [4, 5] xs. The splice (0, 3); / / = > []Copy the code

Slice conforms to the definition of a pure function because it is guaranteed to return the same output for the same input. Splice, on the other hand, chews up the array that calls it and spits it out. This has the observable side effect of permanently changing the array.

3. Curry

Curry’s concept is simple: call a function with just a few arguments and let it return a function that removes the rest.

A simple example:

Var curryAdd = (x) => {return (y) => {return x+y} var curryAdd = (x) => {return (y) => {return x+y }} add(1,2) // 3 curryAdd(1) // 3Copy the code

In fact, the implementation principle of Corritization is closure. It divides a multi-parameter function into multiple return functions, and returns a new function to process the remaining parameters for each function called by passing one parameter. In this way, the separation of call and implementation is realized. Now, if you think about pure functions, you might see that they follow the rules of pure functions, that they take an input and return an output, even if the output is another function, it’s a pure function.

The loDash library is available for currizing functions. If you are not familiar with loDash, check out the loDash website: www.lodashjs.com/

var curry = require('lodash').curry;

var match = curry(function(what, str) {
  return str.match(what);
});

var replace = curry(function(what, replacement, str) {
  return str.replace(what, replacement);
});

var filter = curry(function(f, ary) {
  return ary.filter(f);
});
Copy the code

Use the Currie function

// Match space match(/\s+/g)("hello world"); Var hasSpaces = match(/\s+/g); // function(x) {return x.match(/\s+/g)} // Use this hasSpace to do some related processing hasSpaces("hello world"); // [ ' ' ] hasSpaces("spaceless"); // null // Now that we know its return value, we can do further processing with other functions. For example, filter an array value filter(hasSpaces, ["tori_spelling", "Tori Amos "]) with Spaces; // ["tori Amos "] // Save the findSpaces function variable var findSpaces = filter(hasSpaces); // function(xs) {return xs. Filter (function(x) {return x.atch (/\s+/g)})} FindSpaces (["tori_spelling", "Tori Amos "]); // ["tori amos"]Copy the code

4. Composition of functions

If a value passes through multiple functions to become another value, you can combine all the intermediate steps into a single function, which is called “compose of functions.”

var compose = function(f,g) {
	return function(x) {
		return f(g(x));
	};
};
Copy the code

F and g are functions, and x is the value “piped” between them.

An example of a program that operates on arrays:

Const reverseList = (arr) => {return arr.reverse()} const getHeader = (arr) => {return arr.slice(0,1)} const ToUpperCase = (arr) => {return arr.map(ele => {return ele.toupperCase ()})} // Reverse the array and become uppercase const reverseandCase = compose(reverseList, Reverseandcase (["a","b","c"]) // [" c","b"," a"] const getLastOpt = compose(getHeader, ReverseList) getLastOpt(["a","b","c"]) // ["c"] compose(reverseList, toUpperCase)) getUpperCaseLastOpt(["a","b","c"])Copy the code

In the definition of compose, because the function is executed from the inside out, g will be executed before f, thus creating a right-to-left data flow. This is much more readable than nesting a bunch of function calls. If not combined, the getUpperCaseLastOpt function would look like this:

const getUpperCaseLastOpt = (arr) => {
	return getHeader(reverseList(toUpperCase(arr)))
}
Copy the code

If you look at the composition function for the compose method, you may notice that the result is the same whether you invert the array and capitalize it first, or if you take the last item and capitalize it first. This property is called the associative property, which means that whether you group g and h together, It doesn’t matter if you put f and G in the same group. So if YOU cut the last term and make it uppercase you can write it like this:

compose(getHeader, compose(reverseList, toUpperCase))

compose(compose(toUpperCase, getHeader), reverseList)

compose(compose(getHeader, reverseList), toUpperCase)
Copy the code

Composition is also one reason why functions must be pure. Because how do you compose an impure function with other functions? How do you guarantee that it will behave the way you want it to behave?

// In the previous example we had to write two combinations, but since combinations are associative, we can write just one, pass it as many functions as we want, and let it decide how to group it. const getUpperCaseLastOpt = compose(toUpperCase, getHeader, reverseList); lastUpper(['a', 'b', 'b']); // For slightly more complex variable combinations, the generic definitions can be found in libraries like Lodash, underscore, and Ramda.Copy the code

5. Functor (Founctor)

We know that functional programming is a program that passes data between a series of pure functions through pipes. The previous ones are just simple variable processing, but there will definitely be more complex operations in development, such as exception handling, asynchronous operation, and so on. At this time, functors can be used.

We can think of a functor as a container that we put values into and let the container run our own custom function methods. Here’s an example:

Const Container = function (x) {this._value = x} const Container = function (x) {this._value = x} const Container = function (x) {this._value = x} const Container = function (x) {this._value = x} Container. Of = function (x) {return new Container(x)} // container.of ('Hello JS') // Once the Container has the value, Whatever this value is, we need a way for other functions to operate on it. Container.prototype.map = function(f){ return Container.of(f(this._value)) } Container.of(2).map((x) => {return x + 2}).map((x) => {return x * 3})Copy the code

A few famous functors:

Maybe functor

Functors accept various functions and handle values inside the container. The problem here is that the value inside the container may be a null value (such as NULL), and the external function may not have a mechanism to handle null values. If null values are passed in, an error is likely. The Maybe functor is designed to solve this kind of problem. In short, it sets null checking in its map method.

const Maybe = function (x) {
	this._value = x
}

Maybe.of = function (x) {
	return new Maybe(x)
}

Maybe.prototype.map = function(f) {
    return this._value ? Maybe.of(f(this._value)) : Maybe.of(null)
}

Maybe.of("abcdefg").map((x) => {return x.match(/a/ig)})
// Maybe(['a'])
Maybe.of(null).map(match(/a/ig))
// Maybe(null)
Copy the code

Either functor

The conditional operation if… Else is one of the most common operations, expressed in functional programming using Either functors. The Either functor has two internal values: Left and Right. An rvalue is the value used normally, and an lvalue is the default value used when an rvalue does not exist.

const Either = function (left, right) { this.left = left this.right = right } Either.of = function (left, right) { return new Either(left, right) } Either.prototype.map = function(f) { return this.right ? Either.of(this.left, f(this.right)) : Either of (f (enclosing left), enclosing right)}. Either of (1, 2). The map ((x) = > {return x + 1}). Either of (1). The map ((x) = > {return x + 1})Copy the code

The benefits of functional programming

Composition, a function is responsible for only one function, multiple functions combined to have a powerful function. For example, f= compose(f, G, H), for example, compose(f, G, H), for example, compose(f, G, H), for example, compose(f, G, H), for example, compose(F, G, H), for example, compose(F, G, H), for example, compose(F, G, H)

Data immutability, i.e., data cannot be changed, for the thinking of object-oriented programming, data is encapsulated in the object, all the change history are hidden, not easy to debug, and for functional programming, the data is immutable, you pass in a value to a function, the function will only return the value corresponding to the value after the deformation, The original data is immutable.

Pure functions, where the output of a function is related only to the input, have predictable results, which are good for both unit testing and bug reduction

In general, the benefits of functional programming are:

1. Functions are broken down into small functions with a single function

2. Hard coding is dead and more flexible

3. Combined functions and high-order functions are used to flexibly combine each small function

4. The more simple the responsibility, the better the reusability. These small functions can be combined in other places to achieve more functions.

5. Increase the readability of the code. With the increase of the code quantity, the reading and writing of the code has caused trouble to us.

6, convenient debugging, which benefits from functional programming is pure function, no side effects, each time passed in a parameter, the result is unique, will not be external variables or common variables caused by the result is not the result we expected.

Some problems with functional programming

1. It may be used by too few people compared to object-oriented programming. Perhaps because of the barriers to entry, most programming today is still object-oriented.

2. Functional programming often makes heavy use of Corrification, which theoretically affects performance (because it’s all closures) and hence experience.

3. Once you’re used to imperative programming, switching to functional programming can be a bit difficult. People who are used to imperative programming may want to think about it in an imperative way before changing to a functional one.