preface

The concept of front-end functional programming has been around for a long time. I may have more or less used the functional method to write code in my projects, but I have not studied what is functional programming carefully and deeply. Recently, I happened to have some time to check some materials and read some books to summarize my experience.

Definition of higher order functions

If you want to understand functional programming, you have to understand what higher-order functions are.

Higher-order functions are defined as:

Functions can be passed as arguments and functions can be output as return values

Use ES6’s built-in higher-order functions to write code

Suppose we have an array like this:

const classA = [
    {
        name: 'Joe',
        age: 17
    },
    {
        name: 'bill',
        age: 15
    },
    {
        name: 'Cathy',
        age: 16
    },
]
Copy the code

One requirement is to find a class of 16-year-olds, and we use a low-order function to filter like this:

let student = [];
for (let i = 0; i < classA.length; i++) {
    if (classA[i].age === 16) {
        student.push(class[i])
    }
}
Copy the code

Using higher-order functions looks like this:

const student = classA.filter( v => v.age === 16 )
Copy the code

So what are the advantages of using higher-order functions like this? There are two things:

  • First, with the same complexity of code, higher-order functions make implementation much easier
  • Second, higher-order functions can easily split logic

For example, a function that filters students can be split into two parts:

const isAge = v => v.age === 16;
const result = classA.filter(isAge);
Copy the code

After this split, the logic is divided into two parts, the first part is a function to determine age, and the second part is a function to filter the results.

If, in the future, our requirements change, instead of filtering the age of the student, we filter the name of the student, or something else, then all we need to do is change the age function, and the filtering function stays the same.

Well, some people might say, that’s too easy, so try something a little harder!

Suppose we have an array like this:

const array = [['Joe'.'26'.'1000'], ['bill'.'25'.'3655'], ['Cathy'.'30'.'8888']]
Copy the code

We want to change the array to the following form:

[
    {
        name: 'Joe',
        age: '26',
        price: '1000'
    },
    {
        name: 'bill',
        age: '25',
        price: '3655'
    },
    {
        name: 'Cathy',
        age: '30',
        price: '8888'},]Copy the code

Use higher-order functions to do the conversion:

const result = array.reduce((value, item, index) => {
  value[index] = {
    name: item[0],
    age: item[1],
    price: item[2]
  };
  returnvalue; } []);Copy the code

Here we use ES6’s high-order function reduce. For detailed introduction, please refer to the incomplete guide of reduce() method in JavaScript written by Bump Lab

ES6 has built-in higher-order functions, such as filter, Map, reduce, and so on

Ok, at this point, I have some simple concepts about functional programming. What I understand about functional programming is:

When writing code, functional programming is more declarative, while traditional programming is more imperative. For example, the above filtering student age, the traditional programming thinking is, what do I create, what do I loop, what do I judge, what do I get; The idea of functional programming is that I declare a filter function, I declare a judgment function, and I combine the two functions to produce a result.

Write your own higher-order function

After we’ve played with many of the higher-order functions that come with ES6, we can move on to writing higher-order functions, such as a throttling function, functionally,

The throttling function is basically a function that controls the frequency at which events are triggered. Instead of being triggered an infinite number of times a second, it is now limited to 500 milliseconds

throttle(fn, wait= 500) {if(typeof fn ! ="function"// The function throw new TypeError() must be passed."Expected a function"} // TimerletTimer, // whether firstTime = is called for the firstTimetrue; // Arrow functions cannot be used here for context bindingreturn function(... Args) {// First timeif (firstTime) {
            firstTime = false;
            fn.apply(this,args);
        }
        
        if (timer) {
            return;
        }else {
            timer = setTimeout(() => {
                clearTimeout(timer);
                timer = null;
                fn.apply(this, args);
            },wait}}} // If used alone, limit quick and continuous clicks, the button will only be valid every 500ms.'click', throttle(() => {
    console.log('hhh')}))Copy the code

Having written such a higher-order function, we can call it everywhere, such as:

Const add = x => x++; // Const add = x => x++; throttle(add,1000); Const cutDown = x => x--; throttle(cutDown,2000);Copy the code

Now that you understand what higher-order functions are, you need to understand some important concepts of functional programming. Okay

Pure functions

In the concept of functional programming, another important concept is pure functions, so what is a pure function?

We use code to explain what a pure function is:

const z = 10;
add(x, y) {
    return x + y;
}
Copy the code

The add function above is a pure function that reads the values of x and y, returns their sum, and is not affected by the global z variable

Let’s change this function

const z = 10;
add(x, y) {
    return x + y + z;
}
Copy the code

This function becomes impure, because the value it returns is affected by the global z

In other words, the function is affected by external circumstances

So, that’s the first important thing to know if it’s a pure function

1. Pure functions are not affected by the external environment

Use splice and slice again:

Var xs = [1, 2, 3, 4, 5]; / / pure xs. Slice (0, 3); / / = > [1, 2, 3] xs. Slice (0, 3); / / = > [1, 2, 3] xs. Slice (0, 3); //=> [1,2,3] // not pure xs.splice(0,3); / / = > [1, 2, 3] xs. The splice (0, 3); / / = > [4, 5] xs. The splice (0, 3); / / = > []Copy the code

Slice receives the same arguments and returns the same value each time, so it is a pure function

Splice receives the same argument and returns a different value each time, so it is not a pure function

So, we have the second important criterion for whether or not we are pure

Pure function of the same input, will always get the same output

To summarize, the pure function is:

'A pure function is one in which the same input always returns the same output without any observable side effects.'
Copy the code

So what are side effects? In pure functions, there’s a definition:

Anything that happens outside the function itself is called a side effect

In the example above, if the function returns a result that is affected by the external z-variable, the function has side effects, and if the function affects the external environment, it has side effects.

So now that I’ve figured out what a pure function is, it has the following advantages, right

  • Easier to test because their only job is to calculate the output from the input
  • The results can be cached because the same input always gets the same output
  • Self-documentation because function dependencies are clear
  • Easier to call, because you don’t have to worry about the side effects of the function, right

The use of pure functions can greatly reduce the complexity of programming, but the improper use of abstraction for abstraction’s sake can make the code very difficult to understand.

Currie,

The concept of Currization is simple: call a function by passing it a set of arguments and have it return a function to process the rest.

const add = x => y => x + y; add(1)(2); / / = > 3Copy the code

The example above is a typical Cremation function that, when called for the first time, receives the first argument (remember with closures) and returns a new function; On the second call, it takes the arguments passed in the second time and adds them to the function passed in the first time, returning their sum.

This example illustrates a feature, or a basis, of Currization, that is, the currization function has the particularity of delayed evaluation, and this particularity needs some means to achieve.

Use the above ideas to write a Caulization function

// Create a currying function that saves the first argument and function passed, returns a function that accepts the second argument, and calls the function that is passed to count currying (fn,... args1) {return(... args2) => {returnfn(... args1, ... Const add = (x, y) => x + y; const add = (x, y) => x + y; // Use const increment = currying(add, 1); console.log(increment(2)); const addTen = currying(add, 10); console.log(addTen(2)); // => 3 // => 12Copy the code

The return value is not automatically currified.

currying(fn, ... args1) { //'Check whether the parameters passed in meet the requirements of the function passed in. For example, if the add function needs to add two parameters, check whether the parameters passed in meet the requirements of the call to the function passed in.'
  if (args1.length >= fn.length) {
    console.log(args1, '- 1 -);
    returnfn(... args1); } / /'Not satisfied with returning a new function, continue to call the Currified function, pass in the saved function passed in the first time, pass in the saved parameter passed in the first time, pass in the parameter passed in the second time, continue the above judgment logic, return the result of the calculation.'
  return(... args2) => { console.log(args2,'- 2 -);
    returncurrying(fn, ... args1, ... args2); }; }, // define a general function const add = (x, y) => x + y; // Use const increment = currying(add, 1); console.log(increment(2)); const addTen = currying(add, 10); console.log(addTen(2)); / / = > [2] - 2 - / / = > [1, 2], 1, / / = > 3 / / = > [2] - 2 - / / = > [10, 2], 1 - / / = > 12Copy the code

A function is a first-class citizen in JS. It is no different from any other object, or any other data. It can have arrays, objects, assigned to variables, passed around as arguments, so functions also have subscript properties

const add = (x, y) => x + y;
console.log(add.length)
// => 2
Copy the code

In ES6,… It’s an extension operator, and it works like this

// Put it as a single argument to a function, which turns an array into a sequence of arguments. For example, the array [1,2] in the above example becomes arguments x=1,y=2. Args1) // as the second argument to the function, it turns the value passed in to an array. If the value is passed in to an array, it is still an array. If an object is passed in, it becomes an array objectfunctioncurrying(fn,... X) {console.log(x)} currying(0,1) // => [1] // put in a callback function as the second and third arguments // the first call will return a function that stores values in a closure, and the second call will combine the values in the closure and the values in the second argument into an arrayreturncurrying(fn, ... args1, ... args2); [1,2] [1,2] [1,2] [1,2] [1,2function currying(fn,...x,...y) {
	console.log(x)
}
currying(0,1,2)

Copy the code

With that in mind, the above example makes sense.

The more important idea of the Currization function is:

Several times to determine whether the passed parameters meet the calculation requirements, yes, return the calculation result, if not, continue to return a new Coriolization function

Some libraries, such as Ramda, have implemented it. You can look at the source code to see how it is implemented. It is important to understand how the Currization function is implemented.

Code combination

First, write a simple combinatorial function:

const compose = (f, g) => x => f(g(x));
Copy the code

The combined function takes two functions as arguments and returns a new function. X is the value used between the two functions, for example:

Const toUpperCase = x => x.toupperCase (); const toUpperCase = x => x.toupperCase (); const exclaim = x => `${x}! `; const shout = compose(exclaim, toUpperCase); shout('hello world')
// => HELLO WORLD!
Copy the code

Note: in combination functions, g is executed before F, so in combination functions, it is executed from right to left, that is, the first function is placed to the right of the combination function

It can only take two arguments. Let’s tweak it a bit to make it more powerful:

const compose = (... fns) => (... args) => fns.reduceRight((res, fn) => [fn.call(null, ...res)], args)[0]; Const toUpperCase = x => x.toupperCase (); const toUpperCase = x => x.toupperCase (); const exclaim = x => `${x}! `; const head = x => `slice is:${x}`;
const reverse = x => x.slice(0, 7);

const shout = compose(exclaim, toUpperCase, head, reverse)
shout('my name is maya')
// => SLICE IS: MY NAME!
Copy the code

The principle of combination is essentially the associative law of mathematics:

(a + b) + c  =  a + (b + c)
Copy the code

So, in a combination you can do this

// first const one = compose(exclaim, toUpperCase) const shout = compose(one, head, reverse) shout('my name is maya') // => SLICE IS: MY NAME! // second const two = compose(toUpperCase, head) const shout = compose(exclaim, two, reverse) shout('my name is maya') // => SLICE IS: MY NAME! // third const three = compose(head, reverse) const shout = compose(exclaim, toUpperCase, three)'my name is maya') // => SLICE IS: MY NAME! .Copy the code

So, here, my understanding of combination is:

What is a combination? A combination is a mathematical associative law, like building blocks, that connects different functions, that allows data to flow through it

Underscore (lodash, underscore, ramda, etc.)

  // Returns a function that is the composition of a list of functions, each
  // consuming the return value of the function that follows.
  _.compose = function() {
    var args = arguments;
    var start = args.length - 1;
    return function() {
      var i = start;
      var result = args[start].apply(this, arguments);
      while (i--) result = args[i].call(this, result);
      return result;
    };
  };

Copy the code

Use a combination of

Well, now that we have a basic understanding of functional programming, how can we use functional programming to write code? Here’s an example:

Thoughts / / / / pseudo code, for example, we got a request the background data, then we need to screen a few times the data, and get the part, and sort / / data const res = {status: 200, data: [{id: XXX, name: xxx, time: xxx, content: xxx, created: xxx }, ... ] } // Encapsulating the request function const HTTP = XXX; //'The traditional way to write it is this.'Http.post.then (res => get data).then(res => filter).then(res => filter).then(res => fetch part).then(res => sort) //'Functional programming looks like this.'// Declare a filter function const a = curry() // declare an extract function const b = curry() // declare a sort function const c = curry() // combine const shout = Compose (c, b, a) // Use shout(http.post)Copy the code

How to formally use functional programming in a project

I think there are several steps to formally using functional programming in a project:

  • 1. Try using ES6’s built-in higher-order functions first
  • 2, familiar with ES6 built-in higher-order functions, you can try to write several higher-order functions
  • 3. In this process, write code as pure functions as possible
  • 4. Once you know something about functional programming, try something similarramdaLibrary to write code
  • 5. In useramdaDuring the process, you can try studying it in the source code
  • 6. Try to write your own libraries, currified functions, combinatorial functions, etc

Of course, this is just my own understanding. I did not use functional programming completely in my actual projects. My development principles are as follows:

Don’t choose functional programming for functional reasons. If functional programming can help you improve the efficiency and quality of your project, use it; If not, don’t; If you’re not familiar with functional programming, like me, use it occasionally

extension

Functional programming is developed on the basis of category theory, and on the relationship between functional programming and category theory, Ruan Yifeng gave a very good explanation, here copy and paste his article

At its core, functional programming is just categorical operations, the same kind of thing as mathematical logic, calculus, and determinants, mathematical methods that happen to be used to write programs

So, do you see why functional programming requires functions to be pure, with no side effects? Because it’s a mathematical operation, and its original purpose is to evaluate, to do nothing else, otherwise it wouldn’t satisfy the functional algorithm.

The last

Oneself level is limited, have the place of mistake leak, hope big guys point out a lot, light gush at the same time!!

reference

Functional Programming (Part 1) Mostly Adequate Guide to FP Functional Programming (Part 1) Mostly adequate Guide to FP Functional Programming (Part 2) Mostly Adequate Guide to FP Functional Programming