preface

In this series of articles, you will learn how to develop a tool library of your own. You will learn how to use Git, how to build basic projects, how to write code, how to use functional programming, how to use TypeScript, how to use TypeScript, how to use TypeScript, how to use TypeScript, how to use TypeScript, how to use TypeScript. Writing documentation and publishing NPM packages.

You may need the following basics to read the article:

  • Basic use of Git
  • TypeScript
  • ES6

Program source code

Windlike-Utils

Series directory

  1. Develop your own tool library (I) : project building

Why use functional programming

Because functional programming does not change external variables and has a unique output for a fixed input, we can use it regardless of the internal implementation of the function, and it is easy to combine multiple functions to produce the desired function, closer to the expression of natural language.

For example, if we want to implement a function y=f(x)=2*x+1, we usually write it like this:

function f(x) {
    return 2*x + 1;
}

f(1);  / / 3
Copy the code

Functional programming, on the other hand, breaks them up into smaller functions and assembles them:

function double(x) {
    return 2*x;
}

function plusOne(x) {
    return x + 1;
}

plusOne(double(1));  / / 3

// Or there is a better way to write this, which is not implemented yet,
// This is just to write down their call method, which will be covered in the next article
const doubleThenPlusOne = compose(plusOne, double);
doubleThenPlusOne(1);
Copy the code

Pure functions

  • Immutable is the ability to change input arguments and external variables without side effects, ensuring that a function is “clean.”
  • Uniqueness For each input parameter, there is a unique output, kind of like in mathematicsy=f(x), when inputxConstant, outputyIt’s not going to change

This is a chestnut:

const array = [1.9.9.6];

// Slice is a pure function because it does not alter the array and has a unique output for fixed inputs
array.slice(1.2);  / / (9, 9)
array.slice(1.2);  / / (9, 9)

// Splice is not a pure function. It changes the array and outputs different results for fixed inputs
array.splice(0.1);  / / [9, 9, 6]
array.splice(0.1);  / / [9, 6]
Copy the code

Currie (Currying)

To corrize is to call a function by passing it a set of arguments and having it return a function that handles the rest of the arguments. We implemented a plus one function above, but when we need a plus two function, it is very inefficient to write the code to implement it again, so we need to currize, let’s imagine if we can do this:

const plusOne = add(1);
const plusTwo = add(2);

plusOne(1);  / / 2
plusTwo(2);  / / 4
Copy the code

This makes it easy to get the desired function. Here is an implementation of the add function:

function add(a) {
    return function(b) {
        returna + b; }}Copy the code

It’s pretty much what we need right now, but it’s still a little bit inconvenient, and if we wanted to add three or more numbers we might write something like this:

function add(a) {
    return function(b) {
        return function(c) {
            returna + b + c; }}}Copy the code

So let’s think of a more convenient way:

function add(a, b, c) {
    return a + b + c;
}

const curryAdd = curry(add);
const plusOne = curryAdd(1);
const plusOneAndTwo = curryAdd(1.2);

plusOne(2.3);  / / 6
plusOneAndTwo(3);  / / 6
curryAdd(1) (2.3);  / / 6
curryAdd(1) (2) (3);  / / 6
Copy the code

This gives us the freedom to create functions that require different parameters. Here’s how Curry implements it (think about it for those interested) :

  function curry<Return> (fn: Function) :CurryFunction<Return> {
    // Record how many arguments are required for the function passed in
    let paramsLength: number = fn.length;

    function closure(params: any[]) :CurryFunction<Return> {

      let wrapper: CurryFunction<Return> = function (. newParams:any[]) {
        // Take all the parameters out
        let allParams = [...params, ...newParams];

        if (allParams.length < paramsLength) {
          // Return a new function if the number of arguments is not enough
          return closure(allParams);
        } else {
          // Otherwise return the result
          return fn.apply(null, allParams); }};return wrapper;
    }

    return closure([]);
  }
Copy the code

It may be a little hard to understand, but if you don’t understand it, you can skip here and see below

Here is the source code and the header definition

Alternatively, you can use the native bind function to implement currification:

const plusOne = add.bind(null.1);

plusOne(2.3);
Copy the code

Function composition (Compose)

Function composition is the combination of multiple different functions into a new function.

Like this:

// Combine functions from right to left
const doubleThenPlusOne = compose(plusOne, double);

/ / 1 * 2 + 1
doubleThenPlusOne(1);  / / 3
Copy the code
  function compose<Return> (. fn:any[]) : (. params:any[]) = >Return {
    return(... params:any[]) :Return= > {
      let i = fn.length - 1;
      let result = fn[i].apply(null, params);

      while (--i >= 0) {
        result = fn[i](result);
      }

      return result;
    };
  }
Copy the code

Here is the source code and the header definition

Delay output

Sometimes the world is not so nice, and not all code is “clean”, such as I/O operations and DOM operations, because these operations have external dependencies and impact on the outside world. In this case, delayed output is needed to ensure that our function is “clean”, such as the following implementation of the random function:

  function random(min: number = 0, max: number, float: boolean) : () = >number {
    return() :number= > {
      if (min > max) {
        [min, max] = [max, min];
      }
      if (float || min % 1 || max % 1) {
        return min + Math.random() * (max - min);
      }

      return min + Math.floor(Math.random() * (max - min + 1));
    };
  }
Copy the code

For fixed input, it always returns a function that generates a random number that matches the criteria, so we procrastinate to keep our code “clean.” Isn’t that clever? Another advantage of this is that it uses closure mechanisms to remember parameters and cache them so that you don’t have to pass the same parameters again next time:

const createRandomNumber = random(1.100.false);

createRandomNumber();
createRandomNumber();  // Can be repeated many times to generate 1 to 100 random numbers
Copy the code

conclusion

This chapter covers some of the main concepts of functional programming and why it is a good idea to use it to develop a library of tools, because pure functions are “clean” and do not depend on or affect the outside world without worrying about affecting the original code.

In the next section we’ll look at how to write test cases for your own projects.