Functional programming is not the same as functional programming “language”, but rather such a thought and concept.
In the computer field, as the hardware development has reached the physical limit, Moore’s Law has slowly begun to fail. What effect will Moore’s Law have after it expires? That is, the program we wrote can no longer be as before, as long as it waits for 18 months (this is not the point), it can double our processing power or performance.
Therefore, when the development of hardware slowly slows down, and our business scale grows beyond the stand-alone limit, the only way is to distribute the program to different computers and to improve our processing power through multiple computers.
When we put the program on multiple computers to run and form a cluster, if multiple nodes access and modify the same state at the same time, it will cause data consistency problem, which is one of the reasons why distributed programs are so difficult to write.
The same thing happens when we’re writing multi-threaded applications. When we’re using imperative or object-oriented languages, we can modify the state directly. For example, if we have a variable, we can assign it to it at any time.
Of course, if at any time, only one thread can access and modify this variable, there is no problem.
But don’t forget that other threads can also access this variable. When multiple threads read and modify variables at the same time, if there is no protection (such as lock), then the state may be read and calculated incorrectly. Even covered.
- Multithreaded access is a variable.
- A distributed cluster accesses a database or a cache.
what differences are there? In addition to the location of the storage is different, basically the problem is the same, that is, must be coordinated and controlled, the state consistency problem caused by concurrency (whether it is a variable, or database, cache, I collectively call them state ).
Our program has complete control over the state, and can modify the state at any time and anywhere. It is conceivable that if we do not manage the state effectively, it will easily cause confusion and greatly reduce maintenance.
If the effect of global variables is flat, we only need to linearly modify the order of these state codes to solve the BUG; then, with concurrent competition, the complexity of troubleshooting BUG and organization procedures is improved by one dimension. Because space and time are no longer one-to-one correspondence.
Pure function in functional programming
Isn’t there an effective way to solve these tough problems? If you read blogs frequently or pay attention to the latest technical articles and frameworks, you will find that many frameworks are slowly supporting and improving the process of moving from “synchronous” to “asynchronous”.
What is synchronization?
The code we are writing every day is in line with the human brain thinking order, and it is executed from top to bottom, such as x = 2, then x = x * x, and it is easy to draw the conclusion of the last x == 4.
Where x is a variable, it may be stored in the CPU’s registers, or it may be stored in the heap memory, but this is not the point, they are all on the same computer.
Synchronization is performed sequentially, and all the code is executed step by step in the order of the code we write. We use branch judgments and loop statements to control the execution line, relying on the state variables that were previously calculated.
What is asynchronous?
The shortcomings of synchronization are obvious. Since the code is executed strictly in order, this is also the way we expect.
But once it involves IO operations, such as files and networks, the entire program will be blocked, so multithreading can help us to separate the blocking operation from the current process, and then execute the relevant after reading. operating.
We can handle it asynchronously through callbacks or events. The so-called asynchronous, simple and rude understanding is that we call a function, he will not get the result immediately, and the synchronization can get the result, even if the time is long, we wait (blocking).
It is conceivable that asynchronous increases the complexity of the code, because the original synchronization returns the result directly, and asynchronous requires us to wait for the wakeup in another processing unit and then continue.
In addition, synchronous code can only be executed in one CPU, while multi-threaded asynchronous can use other CPUs on the computer to make parallel execution more efficient.
Imagine if we have a function, it will not modify any state, just calculate the parameters, and then return the result of the calculation, then we distribute the function to a different computer to execute, is it not subject to a single computer What is the impact of the ceiling?
Since this function does not modify any state, there are no side effects, so it can be executed anywhere without relying on other conditions. This function is called a pure function.
A pure function is like a unit that can be moved to different places to execute at any time.
Therefore, a pure function can be treated as an asynchronous wake-up processor. We don’t know when it will be executed, but we can rest assured that it does not cause side effects and does not depend on other preconditions.
You may have noticed that the content of this article is actually the Actor model. Yes, you can think of each Actor as a pure function.
But in any case, you are free, you can do anything in the actor execution unit, but remember that pure functions must not cause any external state modifications, which is principle and fundamental, because we don’t want side effects.
So in the purely functional programming “language”, there is no assignment operation at all, but the description describes the input and then returns the result, which allows us to make fewer mistakes.
Now, the processor we have (pure function), it can be executed as an asynchronous processing unit on any computer, then the state?
Imperative vs declarative
What is imperative? What is declarative?
For example, remember why you use Spring? The most basic is because you need dependency injection.
I just need to declare which type of instance a bean needs to depend on. Spring will find it for us and pass it in. This is declarative, and if you instantiate it yourself, you need to find those dependencies. This is imperative.
Don’t call us, we’ll call you.
Since the pure function does not modify the state, it is simply a simple input (parameter) -> calculation -> output (return) IO unit, so there is no assignment operation.
If traditional programming is to manipulate (modify) the state, then the functional programming language is just the opposite. It does not modify the state. It just describes the process of calculation.
Therefore, the traditional programming changes the state is imperative, and the functional programming changes the state to describe the declarative.
If you can’t understand this sentence, think about the Stream API and Lambda expression in Java8. After the decomposition, the state transition, filtering, and collection are called in a declarative way, which is more intuitive and convenient, and can be executed in parallel without modifying the rest of the code.
The specific language, virtual machine execution environment, and framework that implement functional programming will be responsible for state maintenance, as well as orchestrating your pure functions, and being activated at a certain time in a certain place.
Don’t think that what I am talking about is just the “language” of functional programming. In fact, as long as the relevant rules are followed, the use of the framework is the same. What is important is the meaning behind the concept of these rules.
Just like that sentence, how do you say it?
Even with C, we can do object-oriented programming. But if you don’t understand object-oriented, even if you use Java or C++, it is no different from using C.
Maybe we will use the features of these high-level languages, but we can’t understand why we need them. We just implement our requirements according to the language specifications.
One consequence of putting the cart before the horse is that we rely on a specific programming language rather than relying on our thinking and experience.
Erlang, Lisp, Scala, Akka, Vert.x, Reactor, RxJava, etc., whether it’s a language, a framework, or a tool library, can help us reduce the amount of work required for asynchronous programming, but you might ask: What is it used for? Why do we need to be asynchronous?
The answer is that we need to develop distributed applications that can run on different machines and form clusters to provide large-scale concurrent application services, but at the same time, we also need to fully utilize the resources on each computer.
Therefore, we need to give control of resources to these frameworks and give them the theory and practice behind them so that we can stand on the shoulders of giants.
Functional programming is not a silver bullet
Because of cross-network optimization and state management, preferences related to business scenarios cannot be ignored.
We need to organize according to our own situation, in order to maximize the avoidance of problems caused by the defects inherent in the current computer architecture, in exchange for resources at the least cost.
Functional programming allows us to give up some power in exchange for peace under the rules, but it is nothing but a tool. If we can use this tool in the right scene, we can make our work more effective.
With any technology, we can’t get away from the insights of the original business, and only then can we build the best matching application service.
The use of the application scenario will not only increase the cost of post-maintenance, but also make the evolution of the architecture a huge challenge. Unless you really understand what you are doing, we may never get an effective improvement.
If we follow some of the normative constraints of functional programming, we can reduce some errors, because it is precisely because of the existence of these normative constraints that we avoid falling into the quagmire and unable to extricate ourselves.
More and more frameworks are beginning to support and improve asynchronous programming, just like WebFlux from Spring 5, using Reactor (https://projectreactor.io) as the underlying support, bringing the fully asynchronous asynchronous declarative programming paradigm .
Another example is Vert.X (https://vertx.io), a powerful and addictive tool for people. When you browse more and more new open source projects, or the existing open source projects begin to slowly transition. trend.
You will find that many knowledge concepts are generic and can be converted to each other, asynchronous, distributed, non-blocking, event-driven, reactive, etc…
And these are programming knowledge for the future, no matter which language, framework or tool library you use.