Home >Web Front-end >JS Tutorial >JavaScript and functional programming explained_javascript skills
Author: Yueying
Remember: functional programming is not programming with functions! ! !
23.4 Functional Programming
23.4.1 What is Functional Programming
What is Functional Programming? If you ask so bluntly, you will find that it is a concept that is not easy to explain. Many veterans with many years of experience in the field of programming cannot clearly explain what functional programming is studying. Functional programming is indeed an unfamiliar field for programmers who are familiar with procedural programming. The concepts of closure, continuation, and currying seem so unfamiliar to us. The familiar if, else, and while have nothing in common. Although functional programming has beautiful mathematical prototypes that procedural programming cannot match, it is so mysterious that only those with a PhD can master it.
Tip: This section is a bit difficult, but it is not a necessary skill to master JavaScript. If you don’t want to use JavaScript to complete tasks that are done in Lisp, or don’t want to learn such esoteric topics as functional programming Tips, you can skip them and move on to the next chapter of your journey.
So back to the question, what is functional programming? The answer is long...
The first law of functional programming: Functions are of first type.
How to understand this sentence itself? What is a true Type One? Let’s look at the following mathematical concepts:
The binary equation F(x, y) = 0, x, y are variables, write it as y = f(x), x is the parameter, y is the return value, f is the mapping relationship from x to y, which is called a function. If there is, G(x, y, z) = 0, or z = g(x, y), g is the mapping relationship from x, y to z, and is also a function. If the parameters x and y of g satisfy the previous relationship y = f(x), then we get z = g(x, y) = g(x, f(x)). This has two meanings. One is f( x) is a function on x and a parameter of function g. Secondly, g is a higher-order function than f.
So we use z = g(x, f(x)) to represent the associated solution of the equations F(x, y) = 0 and G(x, y, z) = 0, which is an iterative function . We can also express g in another form, remember z = g(x, y, f), so that we generalize the function g into a higher-order function. Compared with the previous one, the advantage of the latter representation is that it is a more general model, such as the associated solution of T(x,y) = 0 and G(x,y,z) = 0. We also It can be expressed in the same form (just let f=t). In this language system that supports the iteration of converting the solution to a problem into a higher-order function, the function is called "first type".
Functions in JavaScript are obviously "type one". The following is a typical example:
Array.prototype.each = function(closure)
{
return this.length ? [closure(this[0])].concat(this.slice (1).each(closure)) : [];
Symbol. It's simple in form and infinitely powerful.
[1,2,3,4].each(function(x){return x * 2}) gets [2,4,6,8], and [1,2,3,4].each( function(x){return x-1}) gets [0,1,2,3].
The essence of functional and object-oriented is "Tao follows nature".If object-oriented is a simulation of the real world, then functional expression is a simulation of the mathematical world. In a sense, its level of abstraction is higher than object-oriented, because mathematical systems inherently have features that are incomparable in nature. of abstraction.
The second law of functional programming: Closures are the best friend of functional programming.
Closures, as we have explained in previous chapters, are very important for functional programming. Its biggest feature is that you can directly access the outer environment from the inner layer without passing variables (symbols). This brings great convenience to functional programs under multiple nesting. Here is an example :
(function outerFun(x)
{
return function innerFun(y)
{
return x * y;
}
})(2) (3);
The third law of functional programming: Functions can be Currying.
What is Currying? It is an interesting concept. Let’s start from mathematics: we say, consider a three-dimensional space equation F(x, y, z) = 0, if we limit z = 0, then we get F(x, y, 0) = 0, denoted as F'(x, y). Here F' is obviously a new equation, which represents the two-dimensional projection of the three-dimensional space curve F(x, y, z) on the z = 0 plane. Remember y = f(x, z), let z = 0, we get y = f(x, 0), write it as y = f'(x), we say that the function f' is a Currying solution of f.
The following is an example of JavaScript Currying:
function add(x, y)
{
if(x!=null && y!=null) return x y;
else if(x!=null && y==null) return function(y)
else if(x==null && y!=null) return function (x)
b = add(2);
var c = b(10);
In the above example, b=add(2) results in a Currying function of add(), which is a function of parameter y when x = 2. Note The characteristics of closures have also been used above.
Interestingly, we can generalize Currying to any function, for example:
function Foo(x, y, z, w)
{
var args = arguments;
if(Foo.length return function()
{
return
args.callee.apply(Array.apply([], args). Concat (array.apply ([], arguments)));
}
Else
Return x y — z*w;
}> Functional programming fourth law: Delayed evaluation and continuation.
//TODO: Let’s consider it again
23.4.2 Advantages of functional programming
Unit testing
Every symbol of strict functional programming They are all references to direct quantities or expression results, and no functions have side effects. Because the value is never modified somewhere, and no function modifies a quantity outside its scope that is used by other functions (such as class members or global variables).This means that the result of a function evaluation is only its return value, and the only things that affect its return value are the function's parameters.
This is a unit tester’s wet dream. For each function in the program under test, you only need to care about its parameters, without having to consider the order of function calls, or carefully setting external state. All you have to do is pass parameters that represent edge cases. If every function in the program passes unit testing, you have considerable confidence in the quality of the software. But imperative programming cannot be so optimistic. In Java or C, it is not enough to just check the return value of a function - we must also verify the external state that this function may have modified.
Debugging
If a functional program doesn’t behave the way you expect, debugging is a breeze. Because bugs in functional programs don't depend on code paths unrelated to them before execution, problems you encounter can always be reproduced. In imperative programs, bugs appear and disappear, because the functions of functions there depend on the side effects of other functions, and you may search for a long time in directions unrelated to the occurrence of the bug, with no results. This is not the case with functional programs - if the result of a function is wrong, then no matter what else you execute before, the function will always return the same wrong result.
Once you recreate the problem, finding its root will be effortless and may even make you happy. Interrupt the execution of that program and examine the stack. As with imperative programming, the parameters of each function call on the stack are presented to you. But in imperative programs these parameters are not enough. Functions also depend on member variables, global variables and the state of the class (which in turn depends on many of these). In functional programming, a function only depends on its parameters, and that information is right under your eyes! Also, in an imperative program, just checking the return value of a function cannot make you sure that the function is working properly. You have to check the status of dozens of objects outside the scope of that function to confirm. With a functional program, all you have to do is look at its return value!
Check the parameters and return values of the function along the stack. As long as you find an unreasonable result, enter that function and follow it step by step. Repeat this process until you find the point where the bug is generated.
Parallel
Functional programs can be executed in parallel without any modification. Don't worry about deadlocks and critical sections because you never use locks! No data in a functional program is modified twice by the same thread, let alone two different threads. This means that threads can be simply added without a second thought without causing the traditional problems that plague parallel applications.
If this is the case, why doesn’t everyone use functional programming in applications that require highly parallel jobs? Well, they are doing that. Ericsson designed a functional language called Erlang and used it in telecommunications switches that require extremely high fault tolerance and scalability. Many people have also discovered the advantages of Erlang and started using it. We're talking about telecommunications control systems, which require far more reliability and scalability than a typical system designed for Wall Street. In fact, the Erlang system is not reliable and easy to extend, JavaScript is. Erlang systems are just rock solid.
The story about parallelism does not stop here. Even if your program itself is single-threaded, the compiler of a functional program can still optimize it to run on multiple CPUs. Please look at the following code:
String s1 = somewhatLongOperation1();
String s2 = somewhatLongOperation2();
String s3 = concatenate(s1, s2);
at In functional programming languages, the compiler analyzes the code to identify potentially time-consuming functions that create strings s1 and s2, and then runs them in parallel. This is not possible in imperative languages, where each function may modify state outside the scope of the function and subsequent functions may depend on these modifications. In functional languages, automatically analyzing functions and finding candidates suitable for parallel execution is as simple as automatic function inlining! In this sense, functional-style programming is "future proof" (even if I don't like to use industry terms, I will make an exception this time). Hardware manufacturers could no longer make CPUs run faster, so they increased the speed of the processor cores and achieved a fourfold speed increase due to parallelism. Of course, they also forgot to mention that the extra money we spent was only used on software to solve parallel problems. A small portion of imperative software and 100% of functional software can run directly in parallel on these machines.
Code Hot Deployment
In the past, when installing updates on Windows, it was inevitable to restart the computer, and more than once, even if a new version of the media player was installed. Windows XP greatly improved this situation, but it's still not ideal (I ran Windows Update at work today, and now an annoying icon always shows up in the tray unless I restart the machine).Unix systems have always run in a better mode. When installing updates, only system-related components need to be stopped, rather than the entire operating system. Even so, this is still unsatisfactory for a large-scale server application. Telecommunications systems must be operational 100% of the time because if emergency dialing fails while the system is being updated, loss of life could result. There's no reason Wall Street firms have to shut down services over the weekend to install updates.
The ideal situation is to update the relevant code without stopping any components of the system at all. This is impossible in an imperative world. Consider that when the runtime uploads a Java class and overrides a new definition, then all instances of this class will be unavailable because their saved state is lost. We could start writing some tedious version control code to solve this problem, then serialize all instances of this class, destroy these instances, then recreate these instances with the new definition of this class, and then load the previously serialized data and hope that the loading code will properly port that data to the new instance. On top of this, the porting code must be manually rewritten for each update, and great care must be taken to prevent breaking the interrelationships between objects. The theory is simple, but practice is not easy.
For functional programs, all state, that is, the parameters passed to the function, are saved on the stack, which makes hot deployment a breeze! In fact, all we need to do is do a diff between the working code and the new version, and then deploy the new code. The rest will be done automatically by a language tool! If you think this is a science fiction story, think again. For years Erlang engineers have been updating their running systems without interrupting it.
Machine-assisted reasoning and optimization
An interesting property of functional languages is that they can reason mathematically. Because a functional language is just an implementation of a formal system, all operations done on paper can be applied to programs written in this language. Compilers can use mathematical theory to transform a piece of code into equivalent but more efficient code [7]. Relational databases have been undergoing this type of optimization for years. There's no reason why this technique can't be applied to regular software.
Plus, use these techniques to prove parts of your program are correct, and maybe even create tools to analyze your code and automatically generate edge cases for unit testing! This functionality is of no value to a robust system, but if you are designing a pace maker or an air traffic control system, this tool is indispensable. If the applications you write are not core tasks in the industry, this type of tool can also give you a trump card over your competitors.
23.4.3 Disadvantages of functional programming
Side effects of closures
In non-strict functional programming, closures can rewrite the external environment (in the previous chapter we have seen), this brings side effects, and when such side effects occur frequently and often change the environment in which the program runs, the error becomes difficult to track.
.
//TODO: