2022, Worth Learning Series: Functional Programming

Original link: https://www.jeffjade.com/2022/06/08/244-2022-worth-learning-series-functional-programming/

After nearly 70 years, functional programming (Functional Programming) is starting to gain more and more attention. And as time goes by and technology evolves, it is more and more closely integrated with front-end development; a few days ago, after reading some articles, I have a superficial understanding of functional programming, and I have done some practice on the code, and found this. This idea is very interesting and delighted. Recently, my goddess was suddenly curious about it, so I combined with the existing sharing in the industry, combined with my own understanding and practice, and sorted it out for easy explanation.

functional programming

Remarks: The original article was first published in: Talking about the understanding of “functional programming” | Yixiang Leisure Pavilion , @May 30, 2022.

Difference from non-functional

The non-functional sample code is as follows:


1
2
3
4

let counter = 0
const increment = () => {
counter++
}

1
2
3

const increment = ( counter ) => {
return counter + 1
}

You might find this example too generic. But, this is the principle of functional programming: do not depend on external data, and do not change the value of external data, but return a new value to you .

Let’s look at a more complex, but more practical code example :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
twenty one
twenty two
twenty three
twenty four
25
26
27
28
29
30
31

const handleSearch = ( _keywords ) => {
let queryResultArr;
if (!_keywords) {
queryResultArr = []
} else {
queryResultArr = getQueryResult(_keywords)
}
setQueryResultArr(queryResultArr)
setIsShowResults(queryResultArr.length > 0 )
}
const throttle = ( fn, wait ) => {
let pre = Date.now ();
return function ( ) {
const context = this ;
const args = arguments ;
const now = Date.now ();
if (now – pre >= wait) {
fn.apply(context, args)
pre = Date.now ()
}
}
}
const requestSearchFunc = throttle(handleSearch, 200 )
const handleInputChange = ( event ) => {
const value = event.target.value;
setKeywords(value)
requestSearchFunc(value)
}

You can use throttle to throttle functions , such as: requestSearchFunc; this technology is actually a Currying technology (decomposes multiple parameters of a function into multiple functions, then encapsulates the function in multiple layers, and each layer of functions returns a function to receive the next One parameter like this, can simplify multiple parameters of the function). From this technique, you may appreciate the idea of ​​functional programming: using functions as variables, focusing on describing the problem rather than how to implement it, can make the code easier to read.

Procedural vs Functional

Procedural programming mainly uses procedure calls or function calls to control the flow; it mainly focuses on solving problems step by step. This is perfectly valid coding, but it has many drawbacks when you want your application to scale. Functional programming, on the other hand, is concerned with describing what to do, rather than how to do it.

For example, for an array of English names, it is necessary to name the dashes and convert them to the big camel case format. Based on traditional procedural programming, you may not think too much, and express your ideas directly in code. Temporary variables and loop statements are used:


1
2
3
4
5
6
7
8
9
10
11
12
13
14

const nameArr = [ ‘nice-jade’ , ‘rayso-lee’ , ‘jon-snow’ , ‘arya-stark’ ];
const newArr = [];
for ( let i = 0 , len = nameArr.length; i < len ; i++) {
let name = nameArr[i];
let tempNameArr = name.split( ‘-‘ );
let newNameArr = [];
for ( let j = 0 , nameLen = tempNameArr.length; j < nameLen; j++) {
let newFormatName = tempNameArr[j][ 0 ].toUpperCase() + tempNameArr[j].slice( 1 );
newNameArr.push(newFormatName);
}
newArr.push(newNameArr.join( ‘ ‘ ));
}
console.log( newArr )
// [ ‘Nice Jade’, ‘Rayso Lee’, ‘Jon Snow’, ‘Arya Stark’ ]

This is the answer to the goal, it’s completely process-oriented ; although when coding, it is possible to bring ideas to the fore. But for readers, it is not very friendly. Because it is mixed with complex logic, filled with a lot of temporary variables, loops, state changes, etc.; usually you need to read it from the beginning to the end to know what it does , and once there is a problem, it is difficult to locate. Of course, you can also split the above code into several functions:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
twenty one
twenty two
twenty three
twenty four
25
26

const nameArr = [ ‘nice-jade’ , ‘rayso-lee’ , ‘jon-snow’ , ‘arya-stark’ ]
const capitalize = ( str ) => str[ 0 ].toUpperCase() + str.slice( 1 ).toLowerCase()
const convertName = ( name ) => {
let tempNameArr = name.split( ‘-‘ )
let newNameArr = []
for ( let j = 0 , nameLen = tempNameArr.length; j < nameLen; j++) {
let newFormatName = capitalize(tempNameArr[j])
newNameArr.push(newFormatName)
}
return newNameArr. join( ‘ ‘ )
}
const getNewArr = ( nameArr ) => {
const newArr = []
for ( let i = 0 , len = nameArr.length; i < len; i++) {
let name = nameArr[i]
const newName = convertName(name)
newArr.push(newName)
}
return newArr
}
const newNameArr = getNewArr(nameArr)
console.log( newNameArr )
// [ ‘Nice Jade’, ‘Rayso Lee’, ‘Jon Snow’, ‘Arya Stark’ ]

This makes it easier to read code with a lot less context to consider. Unlike the first example, it will take you some time to understand if there are no reasonable annotations. After encapsulating the code logic into a function, it is equivalent to: giving a name to each relatively independent program logic, so the code becomes self-explanatory . Fortunately, in this code, the functions are called, and there is no dependency on shared variables , otherwise it would be more complicated. However, it is still full of temporary variables and loops, which increases the difficulty of understanding while increasing the amount of code.

If it is based on functional programming ideas, what kind of code would it be?


1
2
3
4
5
6
7
8
9

const { compose, map, join, split } = require ( ‘ramda’ )
const nameArr = [ ‘nice-jade’ , ‘rayso-lee’ , ‘jon-snow’ , ‘arya-stark’ ]
const capitalize = ( str ) => str[ 0 ].toUpperCase() + str.slice( 1 ).toLowerCase()
const convertName = compose(join( ‘ ‘ ), map(capitalize), split( ‘-‘ ))
const newNameArr = nameArr.map( ( item ) => convertName(item))
console.log( newNameArr )
// [ ‘Nice Jade’, ‘Rayso Lee’, ‘Jon Snow’, ‘Arya Stark’ ]

The above code (implemented with the help of ramda – “a practical functional JavaScript tool library”), although the logic of the program is still divided into functions, but these functions are functional. Because they have three symptoms:

  1. There are no shared variables between them.
  2. Data is passed between functions through parameters and return values.
  3. There are no temporary variables in functions.

From this programming idea, it can be clearly seen that the thinking process of functional programming is completely different. Its focus is on functions , not processes . It emphasizes: solving problems through the combination and transformation of functions, rather than through What kind of statement to write to solve the problem; when your code is more and more, the splitting and combination of this kind of function will produce more powerful power.

What functional programming?

Functional programming , also known as functional programming or functional programming, is a programming paradigm that treats computer operations as functional operations and avoids the use of program state and mutable objects. Compared with imperative programming, functional programming emphasizes the results of program execution rather than the process of execution; it advocates the use of several simple execution units to make the calculation results progressively progressive, and to derive complex operations layer by layer, rather than designing a complex execution process.

In functional programming, a function is a first-class object, which means that a function can be used as an input parameter value to other functions, can also return a value from a function, be modified, or be assigned to a variable.

Not only is the oldest functional language Lisp rejuvenated, but new functional languages ​​are emerging, such as Erlang, clojure, Scala, F#, and more. Currently the most popular Python, Ruby, and Javascript have strong support for functional programming. Even the old-fashioned object-oriented Java and process-oriented PHP are busy adding support for anonymous functions. There are more and more signs that functional programming is no longer a favorite of academia and is starting to make strides in the industry.

Perhaps after “object-oriented programming”, “functional programming” will become the next programming paradigm (paradigm). I am afraid that future programmers must know a little bit more or less. ——Introduction to Functional Programming @ Ruan Yifeng (2012)

A function is a description of the conversion relationship between sets and sets. The input through the function will return one and only one output value. A function is actually a relationship , or a mapping, and this mapping relationship is组合. When the output type of one function is known to match the input of another function, they can be combined. convertName function as mentioned in the above code.

In the programming world, the logic that needs to be processed is actually only “data” and “relationship”, and a relationship is a函数. Once the mapping relationship (function) is found, the problem can be solved; the rest is to pass the data through this relationship and then convert it into another data.

Features of functional programming

Functions are “first class citizens”

The so-called “first class citizen” (First Class) means that functions are on an equal footing with other data types, and can be assigned to other variables, passed into another function as a parameter, or used as the return of other functions. value. As in the code above:


1

const convertName = compose(join( ‘ ‘ ), map(capitalize), split( ‘-‘ ))

Stateless and data immutable

Statelessness and Immutable data are the core concepts of functional programming:

  • Immutable data : It requires all your data to be immutable, which means that if you want to modify an object, you should create a new object to modify, not modify an existing one.
  • Stateless : It mainly emphasizes that for a function, no matter when you run it, it should give the same input and give the same output as the first time it is run, completely independent of external state changes.

To achieve this goal, functional programming proposes what functions should have: no side effects and pure functions.

no side effects”

The so-called “side effect” refers to the interaction between the inside and the outside of the function (the most typical case is to modify the value of a global variable), resulting in other results than the operation.

Functional programming emphasizes that there are no “side effects”, which means that the function must remain independent, all functions are to return a new value, and there is no other behavior, especially the value of external variables must not be modified.

Quote transparency

Referential transparency means that the operation of the function does not depend on external variables or “state”, but only on the input parameters. Whenever the parameters are the same, the return value obtained by the reference function is always the same.

With the third and fourth points above, this is obvious. In other types of languages, the return value of a function is often related to the system state. Under different states, the return value is different. This is called “reference opacity”, and it is very detrimental to observe and understand the behavior of the program.

lazy execution

The so-called lazy evaluation (Lazy Evaluation) means that the function is only executed when it is needed, that is, no meaningless intermediate variables are generated. like above ? For example, the biggest difference between functional programming and imperative programming is that there are almost no intermediate variables, it is writing functions from beginning to end, and only at the end does it produce actual results by calling convertName .

tail recursion optimization

Iteration is often accomplished by recursion in functional languages, and the use of recursion to implement the mechanism of control flow is a very important feature of functional programming. I think of course you know the evil of递归, which is that if the recursion is deep, the stack can’t stand it, and it will cause a huge performance drop. So, we use a tail recursion optimization technique – the stack is reused for each recursion, which improves performance. The implementation of tail recursion is actually based on the recognition and optimization of the compiler. When the compiler finds that a function is tail recursion, it will implement it as an assembly code similar to the iteration in imperative programming.

Functional programming related technologies

  • map & reduce : Needless to say, the most common technology in functional programming is to perform Map and Reduce operations on a collection. This is easier to read in code than a procedural language. (Traditional procedural languages ​​need to use for/while loops, and then reverse the data in various variables) This is very similar to the foreach, find_if, count_if and other functions in STL in C++.
  • pipeline : This technique means that the function instance is made into actions one by one, and then a group of actions is put into an array or list, and then the data is passed to the action list, and the data is sequentially processed like a pipeline. Each function operates, and finally gets the result we want.
  • compose : It can receive multiple independent functions as parameters, and then combine these functions in series, and finally return a “composed function”. What pipe and compose have in common is that both return “combination functions”, the difference is that the order of execution is different, the former is executed from left to right, and the latter is executed from right to left.
  • recursing Recursion : The biggest advantage of recursion is to simplify the code. It can describe a complex problem with very simple code. Note: The essence of recursion is to describe problems, which is the essence of functional programming.
  • currying : Decomposes multiple parameters of a function into multiple functions, and then encapsulates the function in multiple layers, and each layer of functions returns a function to receive the next parameter. In this way, multiple parameters of the function can be simplified. In C++, this is much like bind_1st or bind2nd in STL.
  • higher order function : The so-called higher-order function is a function that takes a function as a parameter, encapsulates the incoming function, and then returns the encapsulated function. The phenomenon is that functions are passed in and out, just like object-oriented objects are flying all over the sky.

The meaning of functional programming

  • The code is concise and the development is fast : a large number of functions are used, which reduces the repetition of the code, so the program is relatively short and the development speed is fast;
  • Close to natural language, easy to understand : functional programming has a high degree of freedom, and can write code that is very close to natural language;
  • More convenient code management : it does not depend on or change the state of the outside world, as long as the input parameters are given, the returned result must be the same;
  • Less error probability : Because each function is very small, and the same input can always get the same output, the test is also very simple;
  • Ease of “concurrent programming” : because it does not modify variables, there is no problem of “locking” threads at all;
  • Hot upgrade of code : Based on the characteristics of no side effects, as long as the interface is kept unchanged, the internal implementation is irrelevant to the outside.

functional programming, epilogue

As you can see in functional programming, it wants to use small (ideally pure ) functions to solve problems. This approach is also very扩展性and functions can be重用.

There is no better and worse paradigm. An experienced developer can see the advantages of each paradigm and choose the relatively more appropriate one for a given problem. Procedural programming doesn’t mean you can’t use functions; functional programming doesn’t prevent you from using “classes” either. These paradigms just help solve the problem in a way that is beneficial as the code grows.

Referenced series of articles

Guess articles you might be interested in

This article is reprinted from: https://www.jeffjade.com/2022/06/08/244-2022-worth-learning-series-functional-programming/
This site is for inclusion only, and the copyright belongs to the original author.

Leave a Comment