Foreword: just meet share meeting, think to think, decide dish once “function type programming”. I’m sure most of my friends, not just me, have an impression of functional programming: She’s supposed to be beautiful, but she wears a veil. So I decided to take a few days to think about it. It would be nice to see the bridge of my nose even if I didn’t lift the veil 🤪 the article is very subjective haha, if there are any mistakes, thanks for raising it.

Functional programming

Q: What do I think of functional programming?

Met her for the first time, is on the way to prepare for the interview, in a hurry has been enchanted ❤️.

If I did everything in a single-minded way before I met her, I began to try to realize what I wanted in a more beautiful and efficient way after I met her.

The “she” in this case is functional programming, which is a paradigm that can be understood as a recognized typical coding style. The coding style of functional programming itself is not the most attractive to me, but the moment I try to use it, it can train me to try to abstract the problem and find a more efficient solution, and this process makes me feel satisfied.

If you’re still wondering where functional programming is worth it and looking around for an answer, stop, learn about it, and use it. You might get it. You might fall in love with it.

Q: Why does JS emphasize functional programming?

JS function itself is the first citizen, the most critical it can be used as a parameter, this unique condition will naturally be associated with it. We can not use the function in JS development, since the use, naturally in the development of looking for a better way to use, in my opinion functional programming is a kind of experience of how to better use the function, it is not to replace, but to strengthen. As for some articles on the Internet, it is not necessary to compare it with other programming styles. For programmers, it is just a programming style, with its own advantages and disadvantages. It is enough to understand and use it in the right place.

Q: What’s different about functional programming?

The fundamental difference, in my opinion, is that it requires you to abstract the object of the problem, find a universal solution, and use it to solve the problem. Before we encounter it, we might struggle to understand how each step of each specific object should be performed.

An example is to determine whether the sum of two numbers is even

let sum = a + b;
let isSumEven = sum % 2= = =0;
console.log(isSumEven); // Each statement emphasizes that you know how to add and how to judge even numbers
Copy the code

So suppose you don’t know how to add, and you don’t know how to judge even numbers 🤓, what do you do?

GetSum = isEven; getSum = isEven; getSum = isEven;

let isSumEven = isEven(getSum(a, b));
console.log(isSumEven);
Copy the code

You don’t know how to add or determine whether the sum of c and D is even, but you do know that there are two utility functions that declare that you can do it, and you continue to use them:

isSumEven = isEven(getSum(c, d));
console.log(isSumEven);
Copy the code

GetSum and isEven here are generic utility functions encapsulated by your partner by abstracting its processing object. In actual coding, your partner here may be a third-party tool library (Ramda, Lodash, etc.), of course, it may also be your colleague, or yourself.

There are two nouns: imperative programming, where you know and write down every step of the way, versus declarative programming. The declarative is that you don’t have to write it down and you don’t even have to know how it’s done internally, you just have to know what you’re trying to achieve and what tools and functions you’re using to get there.

In my opinion, the process of using functional programming actually trains you to think and solve problems in higher dimensions.

Q: More use of function = functional programming?

Functional programming emphasizes the use of functions, so I think this is fine, but there are some accepted standards (a treasure trove of people who have been there before) that you should try to meet if you want to write good functional programming code.

Q: What is the standard for functional programming?

To be honest, I was trying to put together some criteria, but it really boils down to one thing: code as much as possible by combining pure functions. To understand this, you need to understand the two very important concepts of “pure functions” and “combinations.”

2. Pure functions

Q: What is a pure function?

A pure function is literally a “pure function” whose job is to receive the desired result through internal processing. To achieve “pure”, the following conditions need to be met:

  1. There should be no external dependencies inside a function
  2. No operation should be performed inside a function that is unrelated to the function’s responsibility
(1) No external dependencies -> The same input parameters must get the same output results

If the function does not depend on external variables, the output of the function will depend entirely on the input parameters, and a step-by-step machine, using the same input parameters in the same calculation process statement will inevitably output the same result. If external variables are involved in the calculation, the same input parameter may also produce unexpected results, as shown in the following example:

let depend = 0;
const add1 = (a, b) = > depend + a + b;
console.log(add1(1.2)) / / 3. depend =1;             // If somewhere accidentally changes the dependency value.console.log(add1(1.2)) // 4 Inconsistent output results after the same execution statement with the same input parameter due to dependence on external variables
Copy the code
(2) The function does not perform operations beyond its responsibility -> The function has no side effects

A side effect is a function that, in addition to returning a result (responsibility), has additional effects in the calling environment, often as follows:

  • Changed values outside the function (most common)
  • I/O operations (console type)
  • Throws an exception, aborts an error
  • Ajax operation

.

For a more practical example (think of these examples at 🤯 for real understanding), here is the program that calculates consumption:

let ratio = 1;                      // A discount rate of 1 = no discount
const spend = (price, amount) = > {  // This is a function that calculates the consumption value (price- price; The amount - number)
    if (amount > 10) ratio = 0.8;   // There is another operation here, but when the number is greater than 10, 20% off
    return price * amount;
};
Copy the code

In the above example, the function of spend is to calculate the price of a single product × its quantity. However, the function of Spend has an unrelated operation, which determines whether a discount is needed based on the quantity, so as to modify the external ratio. This is the “side effect” mentioned above. However, there is an operation that has an external impact (modifying external variables). In the above example, to remove side effects, you would change to:


const getSpendAndRatio = (price, amount) = > {
    let ratio = 1;
    if (amount > 10) ratio = 0.8;
    return {                        // The calculated discount is also outputTotal: price * amount, ratio: ratio}; };Copy the code

After the above code changes, there are no side effects other than the output. Here’s another thing: if you want to eliminate function side effects, consider turning the actions that cause side effects into results as well. This is like you also make it of sideline business, that no one will say you not business 🤪.

It can be said that a pure function is just a description of the calculation process from one value to another value, and its only responsibility is to follow a fixed flow when calling, calculate the input parameter and the fixed internal parameter and produce a result, not to perform other irrelevant operations.

Q: Is a function with no input or output pure?

I’ve read a lot of articles, but I don’t get the answer, everyone’s pure function has to involve input and output, but my point of view is to see if it can satisfy the above two points, if there is no input, but it can always output the same result, and no side effects, then why can’t it be called a pure function?

Q: It is impossible to write all functions as pure functions. is that functional programming?

My personal view is that functional programming is not meant to be applied to the whole project, if it can be used in the right place, and give play to its advantages, that others do not care, I would like to call that code is excellent functional programming 😎. This is not rigid rules, but flexible programming style

Q: What is reference transparency?

I’ve seen this concept in a number of articles, but there are different versions of it, and some of them are even difficult to understand. I’ve looked through a number of articles to try to understand it, and the last one is more like this: if a function call can be replaced by its return value, then the function call satisfies referential transparency. If you can’t see it for a moment, take a look at the following example, also using the above example to find the consumption value:

let ratio = 1;
const spend = (price, amount) = > {
    if (amount > 10) ratio = 0.8;  // But for quantities larger than 10, we can give 20% discount
    return price * amount;
};
let result = ratio * spend(1.20); / / 16;
Copy the code

Spend (1, 20); spend (1, 20);

let ratio = 1;
// const spend = (price, amount) => {
// if (amount > 10) ratio = 0.8;
// return price * amount;
// };
let result = ratio * 20; / / 20;
Copy the code

Spend (1, 20) does not satisfy transparent reference. Instead, let’s use the example above where the side effects have been eliminated:

const getSpendAndRatio = (price, amount) = > {
    let ratio = 1;
    if (amount > 10) ratio = 0.8;
    return{total: price * amount, ratio: ratio}; };const { total, ratio } = getSpendAndRatio(1.20);
let result = ratio * total; / / 16
Copy the code

Replace the getSpendAndRatio(1, 20) expression with its output {total: 20, ratio: 0.8}:

const{total, ratio} = {total:20.ratio: 0.8 };
let result = ratio * total; / / 16
Copy the code

You can see that the final result is the same, so we can say getSpendAndRatio(1, 20) satisfies reference transparency.

When a function has no side effects, it is reference-transparent where it is called. So how to understand the word “transparent”? In my opinion, for functions with no side effects, just because we don’t have to worry about some operations inside it after calling it, we can regard its parameter -> result transformation process as transparent.

“If the call the same function twice with the same parameters, the result is always the same, this is called the reference transparency”, this is “Haskell interest guide” described in the book, but I don’t agree with, because I can’t see “reference” and “transparent” where, if I were a scientist, I will not make such a word to add another practice 🤪 cognitive burden. In addition, “Wikipedia” says pure functional programming completely prevents side effects and provides referential transparency.

Q: What does pure function mean in functional programming?

Pure functions also give us more room for development due to the two features mentioned above:

(1) Results can be inferred & easy to test

This is mainly attributed to the first characteristic: “The same input parameter must get the same output result”. If a function meets this characteristic, we can regard this function as a mapping relation between values f(x) = y, and then we can infer the output result through the input value. In development, it is easy for us to test, by judging whether the predicted value and output results are consistent, to determine whether the function meets the requirements.

(2) Function reuse

This is largely due to feature two, “no side effects,” which allows us to safely use it anywhere without worrying that it will affect external values and cause unexplained problems.

Three, combination

Function combination, if not described in detail, is understood literally as the form of nested function calls:

const getSum = (a, b) = > a + b;
const isEven = (value) = > value % 2= = =0;
let isSumEven = isEven(getSum(a, b));
console.log(isSumEven(1.2));
Copy the code

So isEven(getSum(a, b)) in this example, this nested function call that can process the input values into the final output, that’s one way to combine, but there’s actually a better way to combine pure functions, which IS the Pointfree style of programming that I’m going to talk about, It provides a cooler form of composition in functional programming, what I like to call advanced functional programming, as follows:

const getSum = (a, b) = > a + b;
const isEven = (value) = > value % 2= = =0;
const isSumEven = compose(isEven, getSum); The compose method is one of the utility functions that combines pure functions and executes from right to left
console.log(isSumEven(1.2));
Copy the code

From the perspective of form change:

Func4 (func3(func2(func1(value)))) => compose(func4, func3, func2, func1) (value)Copy the code

The changed form is so refreshing and fascinating.

Q: Is it functional programming to write code that calls pure functions in a nested way?

Yes, nested calls are one way to combine, but if there were a better way to combine, I’m sure we’d all choose the better one.

Q: What is the “compose” in it?

Compose is a utility function that combines pure function procedures. For nested calls, each call to the next function requires the output of the previous step to be passed in as an argument. The compose function combines each of the small operations into a larger operation in order. The initial arguments are passed in, and the compose function executes the operations in order and outputs the results.

The compose function is composed by nested calls, but it provides a more convenient and elegant way to code the compose function.

function compose(. funs) {
  // Unsimplified version, but a little clearer
  funs = funs.reverse()
  return function (val) {
    let result = val
    // The output of a single step is passed as an input to the next function
    for (let i = 0; i < funs.length; i++) {
      result = funs[i](result)
    }
    return result
  }
  / / simplified version
  // return val => funs
  // .reverse()
  // .reduce((stepResult, stepFuc) => stepFuc(stepResult), val)
}
Copy the code

Reverse is also called in order to be consistent with nested calls, which are executed from right to left. If called sequentially from left to right, this is a new composite utility function called PIPE, which is used as follows:

Func4 (func3(func2(func1(value)))) => pipe(func1, func2, func3, func4) (value)Copy the code

The Pointfree programming style is combined to create a better programming experience with a good function naming convention, such as:

const goods = [
  { name: 'hat'.price: 66.amount: 1 },
  { name: 'clothes'.price: 88.amount: 5 },
  { name: 'socks'.price: 10.amount: 3},];const calculateCost = (goods) = > goods.reduce((result, item) = > result + item.price * item.amount, 0);
const discount = (cost) = > cost * 0.8;
const format = (cost) = > '$' + cost.toFixed(2);

const getCostString = compose(format, discount, calculateCost);

console.log(getCostString(goods)); / / $428.80
Copy the code

For compose(format, discount, calculateCost), you can see the process: calculateCost -> discount -> format.

Finish see the example above, the curious friend to ask for certain: the best function can return a result, you used to combination of above functions are all happened to need only one input parameter, if there is need to function more input parameters, as in the example above, how many discount and formatting the currency symbol needs to be configurable in how to get it?

This is the “single input constraint” in function combinations. Since a function can only return a single result, it must pass a single input parameter to the next function, which requires partial function application techniques.

Q: What are partial function applications?

Partial application (or partial function application) is to fix some of the parameters of a function while generating a function that receives the remaining parameters. To make it easier to understand, let’s write another code example:

func(a, b, c) => func(a, b)(c); // a, b are fixed, generating functions that take only c arguments
func(a, b, c) => func(a)(b, c); // a is fixed to generate a function that takes only b and c arguments
Copy the code

Partial function application key on the part of it can be fixed parameter, and fixed with the implementation of our programming is here will be cached parameters, then generate a need to receive the rest of the parameters of the method, finally received the rest of the parameters of the real executive function after operation, and then take the above format (formatting the final cost) function for example:

// Format the last cost, currency - currency symbol, digit - decimal to keep the number of digits
const format = (currency, digit) = > {
    return (cost) = > `${currency}${cost.toFixed(digit)}`
}
// Fix the currency symbol and keep the decimal place
console.log(format('$'.2) (200.888)) / / $200.89
Copy the code

A partial function call can also be used for discount functions:

// Ratio is how much discount
const discount = (ratio) = > (cost) = > cost * (ratio * 0.1);

// Fixed discount level before use
console.log(discount(8) (200)) / / 160
Copy the code

For example, when using the compose composition, we can apply the compose function to a group of functions that require only one input parameter to achieve flexible configuration:

const getCostString = compose(format(A '$'.2), discount(8), calculateCost);

console.log(getCostString(goods)); RMB 428.80 / /
Copy the code

Functions that use “partial function applications” are also called “partial functions”, not only for composition, but sometimes “partial functions” can work wonders, as shown in the following example:

const isType = (type) = > {
  return (obj) = > {
      return Object.prototype.toString.call(obj) === `[object ${type}] `; }}// Generate a function to determine a particular type by caching a fixed type value
const isArray = isType('Array');
const isObject = isType('Object');

// So you don't have to pass the type and value each time
console.log(isArray(goods)); // true
Copy the code

Four, supplement

In fact, I think this is enough for “understanding and knowing how to use functional programming.” Those of you who read functional programming might wonder why everyone else talks about higher-order functions, Currization, and so on, while I don’t.

In the process of combing through the knowledge of functional programming, I found that functional programming is not so elusive, and following the sequence of my articles, you may even find it a little easier.

Functional programming is a programming paradigm that builds programs by applying and combining functions; Functional programming is sometimes considered a synonym for purely functional programming. — Wikipedia

Functional programming, after all, is a style of programming, and when you code as pure functions as possible, you’re already writing functional programming.

So once you understand the concepts of pure functions and composition, you are good enough to understand functional programming. I have read a lot of functional programming articles, looking at them spend a lot of space to talk about higher-order functions, Currization and other concepts, which makes me confused, the fundamental reason is that I read again and again can not see how they play a large part in functional programming. After straightening out, however, found that in fact is don’t need to spend too much space to describe their concept itself, only need a simple introduction and clear them is how to play a key role in the functional programming is ok, then I will introduce and talk about their associated with functional programming, as for the detailed introduction I think need not to write, these online which are 😂.

Of course, there are some articles are combined with mathematical knowledge in talking about functional programming, these are more advanced, beginners might as well only programmer’s point of view to learn, anyway, I see functor that I also can’t 🤣, first in the simplest way to know it, if there is a chance, and then in-depth understanding and summary. If you can’t understand these articles, forgive yourself.

Q: The relationship between higher-order functions and functional programming

Higher-order functions are those that take one or more functions as input arguments or output a function. Higher-order functions satisfy at least one of the following requirements:

  • Accepts one or more functions as input parameters
  • Output a function

In functional programming, utility functions for “composing pure functions” (compose, PIPE) are higher-order functions that take multiple executive functions and combine them into a new executive function. We also use higher-order functions to implement partial function applications. By using higher-order functions (combined with closures) to cache partial data, a new function is generated to receive the rest of the parameters.

There is no higher order function, so we can only realize the combination of functions through function nesting. So in conclusion, higher-order functions are most closely related to functional programming:

The “partial function application” technique, combined with the closure caching of partially passed parameters, eliminates the “single input constraint” on function composition and makes Pointfree a beautiful programming style.

Using higher-order functions, we can also obtain the operation results in a certain execution process of the combination. The specific operations are as follows:

const getMiddleResult = (func) = > {
    return (preStepResult) = > {
        const middleResult = func(preStepResult);
        // Print the result before proceeding to the next step
        console.log(middleResult);
        return middleResult;
    };
}
const getCostString = compose(format(A '$'.2), discount(8), getMiddleResult(calculateCost));

console.log(getCostString(goods)); RMB 428.80 / /
Copy the code

Q: Function Corrification and functional programming

A function corrification is a special use of a function. Currization is a technique for converting a function that takes more than one parameter into a sequence of functions that takes only one parameter. Code examples to understand:

func(a, b, c) => func(a)(b)(c);
Copy the code

Feeling and “part of the function application” is very similar, so it also leads to most people are confused, I read baidu Encyclopedia, Emmm🤨. When a function has only two arguments, “partial function application” has the same effect as “Currization”, so it is not surprising that people confuse it.

Functions currie and some applications are based on the higher-order functions to be realized, so rather than “higher-order functions” bring honor to functional programming is far less, but it also had the effect of a polish, detailed introduction on everyone should refer to the article, online all write very clear.

Q: Are there any downsides to functional programming

With all the advantages mentioned, there must be some disadvantages. There are two obvious disadvantages:

  • Performance issues Due to the pure functional programming function pay attention to the characteristics of “no side effect”, can’t change the value of the external cause incoming parameters will be a copy of new data to perform the process again, a small amount of data also is not the problem, but once the mass reference type is the value of the need to implement the related operations, will take up a lot of memory, lead to performance problems.
  • There may be excessive wrapping because of the emphasis on the use of functions, it is inevitable that there will be some unnecessary wrapping, like writing a random traversal to show the whole recursion, it is not necessary. In addition, recursion is also an important concept in functional programming, which is to make the function call itself. I believe that you know functional programming, always can not not know recursion ~ but in practice, it is important to pay attention to the use of time.

So learning functional programming isn’t about getting rid of imperative programming. Imperative programming gives you more control over execution, which in turn allows you to handle performance issues better.

Five, the last

Although refer to a lot of literature, but in order to facilitate the understanding of functional programming, this article is relatively strong subjectivity, if there is a mistake, please forgive me, and trouble to put forward in the comments section, thank you for reading.

End