Lift the veil on functional programming

What is functional programming

Functional programming

It is not a new concept, having been introduced in the 1930s as a set of formative systems for studying function definitions, function applications and recursion.

Functional programming is a form of programming that treats computer operations as if they were functions. The most important foundation of functional programming languages is lambda calculus, and functions of the lambda calculus can accept functions as inputs (parameters) and outputs (return values).

Functional programming is a “programming paradigm”, a methodology for how to write programs. It is a form of “structured programming”, the idea being to write operations as a series of nested function calls.

2 category theory

2.1 an overview of the

  1. We can think of the category as a container containing two things. Value and the deformation relation of value, that is, function.
  2. Category theory uses functions to express the relationships between categories.
  3. With the development of category theory, a whole set of operation methods of functions have been developed. This method was originally used for mathematical operations, but later someone implemented it on a computer and it became what is known today as functional programming.
  4. 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. Why does functional programming require functions to be pure and free of 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.

For example, 2.2

Lifting the veil of functional programming. PNG

const A = 'kane';
const B = A.length; // Corresponds to the f operation
const C = B > 0; // Corresponds to the g operation
// Then A -> C can be converted as follows
C = A.length > 0; // Corresponding to the h operation
Copy the code

Functional programming is not a simple function to program, is different from the traditional procedural programming, the main purpose is to synthesize simple functions into complex functions, operation process as far as possible in a series of nested function calls.

Why use functional programming

2.1 features

Functional programming has a lot of great features that other programming paradigms don’t have enough of.

  1. The function is “first-class citizen”. Each function can be regarded as a variable, and the function can be passed like a variable, and can also be used as the return value of the function like a variable, thus forming a higher-order function, which makes the function have more powerful capabilities.
  2. The status cannot be modified. Functional programming requirements are stateless, the implementation of each function is a separate process, avoids the request of a state of the object oriented programming, the bad effects of stateful is good must maintain the current status, and as the program is more and more big, the modified state places more and more, if handled well, It is difficult to trace which action affected the current state.
  3. Reference transparency. Functions do not depend on external variables or states, that is, the output of the function is only related to the input of the function. Whenever the function is executed, as long as the parameters remain unchanged, the result remains unchanged. In other words, the program can run normally if a specific variable replaces the result returned by the function execution.
  4. Testability. This benefit from the functional programming at the same time pay attention to pure and has side effects of separated operation, at the time of test operation, we only need a mock have side-effects to test the stability of the software, and other programming paradigm, there may have side effects of operation are operating together with no side effects, and so cannot be better to complete a single measurement.
  5. There are no side effects. In functional programming, all operations do not affect external variables or change input values.
  6. Be more flexible. Benefit functions can act as “first-class citizens”. Small functions can be combined into more powerful functions to handle more complex business logic, and as long as the small functions are reliable, the combined functions must be reliable as well.
  7. Lazy evaluation. Functional programming can pass in some parameters, then return a new function, and only trigger the execution of the function when it is used. This is also very important. It makes better use of the existing CPU computing resources, otherwise the function will not be executed.
  8. Immutability. Functional programming also advocates immutability of data, that is, a variable is created with its value unchanged, and if it needs to change its value, it needs to change its corresponding address, so that it does not have to worry about using the variable in other functions. This operation requires that we make a deep copy every time we reassign a reference value, but it is too expensive to make a deep copy of js every time. We propose a library that handles immutable data **immutable**.

2.2. Related concepts

2.2.2 pure functions

2.2.2.1 overview

Pure function refers to the function without side effects, the same input corresponds to the same output, and has no impact on the external environment, neither modification of global variables, nor dependence on global variables. This statement is very important, and most of the other features are a derivative of this feature.

2.2.2 for
/ / pure functions
let num1 = 0 ;
function add(num2) {
  return num1 + num2;
}
// an impure function
function add(num1, num2) {
  return num1 + num2;
}
Copy the code
2.2.2.3 Possible side effects
  • Changing global variables
  • Change the parameters
  • Query HTML documents, browser cookies
  • Network request

2.2.3 Currization of functions

2.2.3.1 concept

Is the technique of transforming a function that takes multiple arguments into a function that takes a single argument (the first argument of the original function), and returning a new function that takes the remaining arguments and returns the result.

2.2.3.2 implementation
function curry(fn, ... arg) {
  if (fn.length <= arg.length) {
    returnfn(... arg); }else {
    return function(. innerArg) {
      return curry(fn, ...arg, ...innerArg);
    }
  }
}
Copy the code
2.2.3.3 pros and cons
  • In fact, currification is a method of “preloading” functions by passing fewer parameters to a new function that has already remembered those parameters. In a sense, it is a “cache” of parameters, which is a very efficient way to write functions.

2.2.4 Function Combination

2.2.4.1 concept

By combining multiple functions, return a new function, and the return value of the last function as the parameter of the next function, is a solution to better solve the nesting problem of functions.

2.2.4.2 implementation
function compose(. arg) {
  return result= > arg.reduceRight((result, fn) = > fn(result), result);
}
Copy the code
Advantages and disadvantages 2.2.4.3
  • Through the way of function combination, some small functional functions can be combined into more powerful functions, so as to cope with more complex application scenarios.
  • If the split is very fine, the reusability increases, but the corresponding functions also increase, that is, the increased reusability brings additional development costs.

2.2.5 Point Free

2.2.5.1 concept

In the world of functional programming, there is a very popular programming style. This style is also called Point-free and is referred to as parameter head, which roughly means the programming style without parameter.

2.2.5.2 example
const str2arr = str= > str.split(' ');
const toUpperCase = str= > str.toUpperCase();
const fn = compose(str2arr, toUpperCase);
fn('kane');
Copy the code

This Point Free style greatly reduces the amount of unnecessary code and makes the logic clear.

2.2.6 Containers and functors

  • Any data structure with map methods can be used as an implementation of functors.

  • Functor A type of container that complies with certain rules.

  • Functor is an abstraction of a function call; we give the container the ability to call the function itself. Put the things into a container, only to set aside an interface map to function outside the container, the map function, we let the container to run the function, so that the container can freely choose when and where to how to operate this function, so that the evaluated with inert, error handling, asynchronous invocation, and so on very characteristics of the leather.

2.2.6.1 Implementation of Monad functor

This is a basic functor structure that contains a static of function that returns an instance of Monad and provides a map function that can manipulate, transform, and transform values inside the functor.

class Monad {
  constructor() {
    this.val = val;
  }
  static of(val) {
    return new Monad(val);
  }
  map(fn) {
    return Monad.of(fn(this.val)); }}Copy the code
2.2.6.2Maybe functor implementation

The Maybe functor differs from the Monad functor in that it provides an internal assertion function that determines whether a transformation can be performed by a function passed in from Map. Once the Maybe functor is determined, its corresponding judgment condition is determined.

class Maybe {
  constructor(val) {
    this.val = val;
  }
  static of(val) {
    return new Maybe(val);
  }
  map(fn) {
    return this.isNothing() ? Maybe.of(null) : Maybe.of(fn(this.val));
  }
  isNothing() {
    return this.val === null || this.val === undefined; }}Copy the code
2.2.6.3 Implementation of Either functor

The either functor is used to handle common conditional branching and is a good way to handle unexpected errors. If the program fails, it will enter the Left functor, and the subsequent map conversion is not carried out, which can effectively prevent the program from executing the wrong code in the case of error. If the program executes normally, it will still enter the Right functor, which does not affect the map conversion later in the program.

class Either {
  constructor(val) {
    this.val = val;
  }
  map() {
    throw new Error('Make sure it\'s overwritten'); }}class Left extends Either {
  constructor(val) {
    super(val);
  }
  static of(val) {
    return new Left(val);
  }
  map(fn) {
    return this; }}class Right extends Either {
  constructor(val) {
    super(val);
  }
  static of(val) {
    return new Right(val);
  }
  map(fn) {
    return Right.of(fn(this.val)); }}Copy the code
2.2.6.4 IO functor

This is a common used to handle dirty operating a functor, at this point to val value is not an ordinary variable, but a executable function, wrapped in a dirty, dirty dirty data from operation only when it is in the final use will pollution subsequent pure operation, is one way to deal with dirty data better.

class IO extends Monad {
  contructor(val) {
    super(val);
  }
  static of(val) {
    return new IO(val);
  }
  map(fn) {
    return IO.of(compose(fn, this.val)); }}Copy the code

Third, the application of functional programming

3.1 Function synthesis cases

Next, I’ll look at the application of function composition in functional programming.

Now I have a requirement, I give you a string, I convert the string to uppercase, and I reverse it.

function multiLine(str) {
  const upperStr = str.toUpperCase();
  const arr = upperStr.split(' ');
  const reverseArr = arr.reverse();
  const toStr = reverseArr.join(' ');
  return toStr;
}
// Of course you can write this, just for example, you should think of each step as a pure operation.
function oneLine(str) {
  return str.toUpperCase().split(' ').reverse().join(' ');
}
Copy the code

Now we have changed the requirement to convert the string to uppercase, reverse the string, and assemble the string into an array. For example, ‘ABC’ will end up as [‘C’, B’, ‘A’].

function stringToUpper(str) {
  return str.toUpperCase();
}
function stringReverse(str) {
  return str.split(' ').reverse().join(' ');
}
function strToArr(str) {
  return str.split(' ');
}
const strToUpperArr = compose(strToArr, stringReverse, stringToUpper);
console.log(stringToUpper('abc'));
Copy the code

It can be seen that there are some application scenarios only the order of function calls is different, and different functions can be realized through different combinations of functions, which can maximize the reuse of existing code, which is also an important reason for the popularity of functional programming.

3.2 The Case of Corrification

Let’s say you own a store and you want to give a discount to the members of your store, 10% off the items you sell.

function discountFun(price, discount) {
  return price * discount;
}
const price = discountFun(500.0.9);
function disCountCurry(discount) {
  return price= > {
    returndiscount * price; }}const tenPercentDiscount = disCountCurry(0.9);
const twentyPercentDiscount = disCounyCurry(0.8);

console.log(tenPercentDiscount(500));
Copy the code

3.3Maybe functor cases

Get a User object, filter the age of the object, and continue.

const oper1 = x= > {
  return { age: x. age };
}
const oper2 = x= > x.age;
const oper3 = x= > {
  console.log('I\'m ' + (x + 1) + 'next year');
}
const composedOper = compose(oper3, oper2, oper1);
const mapCurryed = curry((f, functor) = > functor.map(f));
const needOper = mapCurryed(composedOper);
needOper(Maybe.of({name: 'kane'.age: 18}));
Copy the code

3.4 Case of Either functor

const getAge = user= > (user.age === undefined ? Left.of('Error') : Right.of(user.age));
getAge({name: 'kane'}).map(x= > x + 1);
// Left('Error');
getAge({name: 'kane'.age: 18}).map(x= > x + 1);
// Right(19);
Copy the code

It can also be seen that Either functor is more flexible than Maybe functor because its assertions are specified at runtime.

3.5IO functor Cases

const readFile = filePath= > {
  return IO.of((a)= > fs.readFileSync(path.resolve(__dirname, filePath)).toString());
}

const print = content= > console.log(content);
const getLastChar = str= > str[str.length - 1];
const ioCon = readFile('./test.txt').map(getLastChar).map(print);
ioCon.val();
Copy the code

Only when val is called at the end will the program read the file and then proceed with the following operations. This ensures that print and getLastChar are pure and can be tested better.

Four,

4.1 COMPARISON between FP and OOP

4.4.1 FP

  • Simple code, fast development

  • Close to natural language, easy to understand

  • Easier code management

  • Easy “concurrent programming”

4.1.2 OOP

  • Object uniqueness
  • abstract
  • inheritance
  • polymorphism

Object orientation is to make the code easier to understand through the encapsulation of functions. Functional programming makes the code easier to understand through minimal changes. After changing requirements, the internal logic is not changed, but the function is reorganized

4.2 conclusion

  • Functional programming is becoming more popular and easier to use on the front end for more efficient development
  • Functional programming makes code simpler, code more streamlined, and more reliable
  • Functional programming is an idea that is not constrained by programming languages
  • It is impossible to develop all of a program in the functional programming paradigm, that is, the entire program cannot be pure operations

Five, the reference

  1. zhuanlan.zhihu.com/p/21926955