Actual combat background
I have already written 7 articles on JS functional programming, 2 on function composition, and 2 on getting started with Haskell. You can find them in my JS column if you want to read them.
However, this is just the beginning 🏃. Why does Ben Love functional programming, even as a lifeline for front-end projects?
The reason is: Bengua is in a large Web project (I think), each version is almost coordinated by 5 or 6 front-end development, counting the left before and after, the project is currently handled by 10+ front-end hands; Due to “historical reasons”, my personal assessment of the current project generally has these problems:
- Lack of logical encapsulation: It’s hard to see what the business design is like at the code level (don’t always blame the product 🐶);
- Lack of common method encapsulation: the same function by different hands of development, there are many different ways to achieve;
- Debugger difficulty: troubleshooting needs to comb a long process, during which many pits, small judgments;
- Refactoring is difficult: there are numerous interference between codes, state management is chaotic, and there are many implicit inputs and outputs;
- Slow response to requirements: sometimes requirements change quickly and the code cannot respond quickly, so the logic has to be temporarily stacked, increasing the risk of hiding bugs, resulting in a vicious cycle of difficult follow-up;
- The other;
To sum it up: Three ingredients of quality code — readable, maintainable and easy to change — seemed to be unrelated
Always find a solution! Is it ok to enter TypeScript? It should be, strong typing is important for large projects!
However, the experience of Vue2 + TS is poor (I haven’t seen a good practice project so far, if there is, please send it to me 😳), how about upgrading Vue2 to Vue3 first, and then to TS?
It’s too difficult 😫! The project itself is large, and it is not easy to upgrade. It involves manpower, time, learning cost, reconstruction risk, etc. The water is too deep to grasp.
Even more, take a step back and ask: Is strong typing really a good solution to the above problems? !
Maybe there needs to be a higher level of design for dimension reduction!
At this point, functional programming a move “such as god palm”, from the sky, and smile: “This ah, I am familiar with ~”
Let’s take a look at some of the gifts functional programming brings us! 🧐
Talent point 1: Pure functions
What is a pure function? Two definitions:
1. Reference transparency;
2. No side effects;
In mathematics, f(x)=x/2, f(10) is equal to 5 whether it is calculated 100 times or 1000 times;
But in programming, functions do not have this stability.
Function execution depends not only on input values, but also on referenced global variables, input files, class member variables and many other factors.
int counter = 0
int count(){
return ++counter;
}
Copy the code
If the return value of a function depends only on its input value, we call this feature referential transparency.
What do you mean “no side effects”?
When a function is called, only the return value of the function is changed outside the function.
In fact, in a truly functional programming language, side effects are mostly avoided, but in JS this feature is guaranteed only by the habit of the programmer:
- After the input parameter of the function is entered, the parameter operation is used without modifying it.
- Do not modify variables outside the function, such as global variables.
- The result of the operation is returned externally only through the function;
Separate the side effects from the rest of the program logic, and the software will be easier to extend, refactor, debug, test, and maintain! 😃
This is why most front-end frameworks encourage users to manage state and component rendering in separate, decoupled modules.
Therefore, reference transparency guarantees the input of the function, i.e. the function has no implicit input; If the output of the function is guaranteed, that is, the function has no implicit output, such a function is called: pure function;
Pure functions have other characteristics, such as the combination of pure functions must be pure functions;
Talent point 2: Immutability
Immutability is the core concept of functional programming, without it, the data flow in the program is lossy;
How to understand? That is, functional programming relies on immutable data structures and pure calculations from existing data to obtain new data;
For example, 🌰
- In non-functional programming:
const x = { val: 2 }; const x1 = () => x.val += 1; const x2 = () => x.val *= 2; x1(); x2(); console.log(x.val); / / 6Copy the code
We changed the variable x so that if we changed the call order, the result would be different;
const x = { val: 2 }; const x1 = () => x.val += 1; const x2 = () => x.val *= 2; x2(); x1(); console.log(x.val); / / 5Copy the code
In order to know the result of the function, it is necessary to trace the complete history of the variable.
- In functional programming:
const x = { val: 2 }; const x1 = x => Object.assign({}, x, { val: x.val + 1}); const x2 = x => Object.assign({}, x, { val: x.val * 2}); console.log(x2(x1(y)).val); // 6 console.log(x1(x2(x)).val); / / 5Copy the code
It is important that we use object.assign () to create a new Object instead of modifying x directly!
X2 (x1(y)).val becomes x1(x2(x)).val.
Yes, the order in which functions are combined is still important, but you don’t have to worry about what happens to the external variable X! When you want to call x anywhere else, know that x.val must be 2!
Ps: This is so touching! We are constantly overwriting previous values, constantly modifying values that have changed, referencing values in various asynchronous ambiguities, and modifying values…… In the debugger, it is difficult to track the entire process, or you are afraid to use this variable easily. Instead, you choose to create a new variable and overlay the logic, thinking it is more “safe” (actually more chaotic)
Therefore, eliminating the time dependence of various states in function calls eliminates a large portion of potential errors.
However, const does not guarantee immutable variables! 😨
We can define an object with const and still modify its properties. That’s the magic of JavaScript (ˉ▽ˉ;) .
However, a freeze method ensures that the object’s first level attributes are immutable as well:
const a = Object.freeze({ foo: 'Hello', bar: 'world', baz: '! '}); a.foo = 'Goodbye'; console.log(a)//{foo: 'Hello', bar: 'world', baz: '! '}Copy the code
Yeah, it only guarantees one level of properties, not the next level of properties
const a = Object.freeze({ foo: { greeting: 'Hello' }, bar: 'world', baz: '! '}); a.foo.greeting = 'Goodbye'; console.log(`${ a.foo.greeting }, ${ a.bar }${a.baz}`); // Goodbye, world!Copy the code
In many functional programming languages, there is a special immutable data structure (trie tree, dictionary tree);
In JS, we can use Immutable. JS to make our object depth Immutable! No properties will change!
Talent 3: Function composition
I have written two articles about function combinations:
- “Thank the compose function for making my code 💩 beautiful.”
- From [if…else…] to [chain of Responsibility] and then to [composeAOP], by the way [pass-through] solve ~
Function combination is to combine two or more functions to perform a series of calculations.
A (b(c(x))) injects the output of one function into the input of the next function.
Understanding functional composition The key to understanding functional programming!
In my opinion, the focus of function combination is not to execute in sequence, but to encapsulate the process into a sub-function, and each sub-function is pure as far as possible (because it is difficult to control completely without side effects). The process execution does not interfere with the outside world, and only returns the output result at the end; 😄
Digging 🕳 : Various versions of “function composition” will continue to be output: including parameter passing (in the form of objects, recursively retrieved at each step), interrupt jumping, exception catching, multiple composition, Currization, and so on;
Talent point 4: Higher order functions
JavaScript has first-class functions that allow us to treat a function as data, pass a function as an argument to another function, or return another function from a function, which is a higher-order function;
Higher-order functions usually apply to:
- Use callback functions, promises, monads, and so on to abstract encapsulate, isolate environments, or control asynchronous flows;
- Create programs that can accept multiple data types;
- Create Corrified functions or combinations of functions for reuse;
For example, our usual map() method can be used for any data type:
const double = n => n * 2; const doubleMap = numbers => numbers.map(double); console.log(doubleMap([2, 3, 4])); // [4, 6, 8]Copy the code
We pass double into map() and map it to get a new array of results;
Excavation: The output of this melon is specifically for high-order mapping (Map, filter, reduce).
We can further extend the double function to suit our needs:
const double = n => n.points * 2; const doubleMap = numbers => numbers.map(double); console.log(doubleMap([ { name: 'ball', points: 2 }, { name: 'coin', points: 3 }, { name: 'candy', points: 4} ])); // [4, 6, 8]Copy the code
For example, the commonly used debounce function, throttle function, and compose function combination are all high order functions. Towards the advanced front-end, we must write the high order function, there is no ~ 🐱🏍
Stage summary
In fact, almost every large application code base makes heavy use of functional programming ideas.
Functional programming is a programming paradigm that is a way of thinking about software construction based on some basic defining principles, which are described above:
- Pure functions;
- Invariance;
- Function combination;
- Higher order function;
Imperative code often uses statements such as for, if, switch, throw, and so on to perform some action;
Such as:
const doubleMap = numbers => { const doubled = []; for (let i = 0; i < numbers.length; i++) { doubled.push(numbers[i] * 2); } return doubled; }; console.log(doubleMap([2, 3, 4])); / / [4, 6, 8]Copy the code
Functional programming is a declarative paradigm that abstracts the flow and defines the data flow.
Such as:
const doubleMap = numbers => numbers.map(n => n * 2);
console.log(doubleMap([2, 3, 4])); // [4, 6, 8]
doubleMap([2, 3, 4])
Math.max(4, 3, 2)
Copy the code
The latter code tends to be cleaner, more predictable and easier to test!!
Look forward to the follow-up combined with actual combat to practice these principles! (low low ◡)
Ok, the above is the share ~ writing is not easy, like encourage 👍👍👍👍👍
I am Anthony Nuggets, the public account of the same name, output exposure input, technical insights into life, goodbye ~