Functional programming

concept

Abstract things in the real world and the relationship between things to the program world (extract the operation process)
(elephant)

Personal understanding, for reference only, the following is an example of putting elephants into the refrigerator

  • Object oriented: in code, we manipulate classes and instance objects. The refrigerator is abstracted as a container class, the elephant is abstracted as an object class, the container has the attribute of storage, and the object has the attribute of placement; The container class can be instantiated as a refrigerator object, and the object class can be instantiated as an elephant object. Operate the storage method of the refrigerator and the placement method of the elephant to complete the operation.
  • Functional programming: in code, we operate on methods. The process of putting elephants into the refrigerator is sorted into a set of processes, that is, a set of methods. We don't need to care about what happens in the process. Just input the refrigerator and elephant to get the final result through the internal process.
  • Process oriented: step by step, open the refrigerator, load the elephant, and close the refrigerator.

    // Non functional formula
    let num1 = 2;
    let num2 = 3;
    let sum = num1 + num2;
    console.log(sum);
    // Functional formula
    function add(n1, n2) {
    return n1 + n2;
    }
    let sum = add(2, 3);
    console.log(sum);

    Basic knowledge

    Higher order function

    • You can pass a function as an argument to another function
    • You can use a function as the return result of another function

    // filter high order function implementation
    Array.prototype.filterTest = function (fn) {
    let result = [];
    for (let index = 0; index < this.length; index++) {
      if (fn(this[index])) {
        result.push(this[index]);
      }
    }
    return result;
    };
    
    let array = [1, 2, 3, 4, 5, 6, 7, 8, 9];
    console.log(array.filterTest((value) => value > 5));

    closure

    Essence: when a function is executed, it will be placed on an execution stack. After the function is executed, it will be removed from the execution stack. However, the scope members on the heap cannot be released because they are externally referenced. Therefore, internal functions can still access the members of external functions

    function once(fn) {
    let done = false;
    return function () {
      if (!done) {
        done = true;
        return fn.apply(this, arguments);
      }
    };
    }
    let pay = once((money) => {
    console.log(`Paid ${money}`);
    });
    
    pay(10);
    pay(10);
    pay(10);

    Pure function

    The same input will always get the same output, lodash Is a pure function library.

advantage

  • Because pure functions always have the same results for the same input, the results of pure functions can be cached
  • Pure functions make testing easier (unit testing)
  • parallel processing

    const _ = require("lodash");
    
    function getArea(r) {
    return Math.PI * r * r;
    }
    
    // memoize implementation
    function memoize(fn) {
    let cache = {};
    return function () {
      let arg_str = JSON.stringify(arguments);
      cache[arg_str] = cache[arg_str] || fn.apply(fn, arguments);
      return cache[arg_str];
    };
    }
    let getAreaWithMemory = memoize(getArea);
    console.log(getAreaWithMemory(5));
    
    // let getAreaWithMemory = _.memoize(getArea);
    // console.log(getAreaWithMemory(4));

    side effect

  • Side effects make a function impure. Pure functions return the same output according to the same input. If the function depends on the external state, the same output cannot be guaranteed, which will bring side effects.

currying

When a function has multiple parameters, pass some parameters to call it first (these parameters will never change in the future). Then it returns a new function, receives the remaining parameters and returns the result

const _ = require("lodash");

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

function curry(func) {
  return function curriedFn(...args) {
    if (args.length < func.length) {
      return function () {
        return curriedFn(...args.concat(Array.from(arguments)));
      };
    }
    return func(...args);
  };
}
let curried = curry(getSum);
console.log(curried(1)(2)(3));
console.log(curried(1, 2)(3));

// let curried = _.curry(getSum);
// curried(1)(2)(3);
// curried(1, 2)(3);

Function combination (Point Free mode)

If a function needs to be processed by multiple functions to get the final value, the functions of the intermediate process can be combined into one function at this time

const _ = require("lodash");

// const toUpper = (s) => s.toUpperCase();
// const reverse = (arr) => arr.reverse();
// const first = (arr) => arr[0];
// const f = _.flowRight(toUpper, first, reverse);
// console.log(f(["one", "two", "three"]));

function compose(...fns) {
  return function (value) {
    return fns.reverse().reduce(function (acc, fn) {
      return fn(acc);
    }, value);
  };
}

const toUpper = (s) => s.toUpperCase();
const reverse = (arr) => arr.reverse();
const first = (arr) => arr[0];
const f = compose(toUpper, first, reverse);
console.log(f(["one", "two", "three"]));

Functor

Is a special container, which is implemented by an ordinary object. The object has a map method. The map method can run a function to process the value.

  • A functor is an object that implements a map contract
  • The map method returns a functor containing the new value

    Functor

    class Container {
    constructor(value) {
      this._value = value;
    }
    
    map(fn) {
      return Container.of(fn(this._value));
    }
    
    static of(value) {
      return new Container(value);
    }
    }
    
    let result = Container.of(5)
    .map((x) => x + 1)
    .map((x) => x * x);
    
    console.log(result._value);

    Maybe

    Handle external null values to prevent null value exceptions

    class Maybe {
    constructor(value) {
      this._value = value;
    }
    
    map(fn) {
      return this.isNull() ? Maybe.of(null) : Maybe.of(fn(this._value));
    }
    
    isNull() {
      return this._value === null || this._value === undefined;
    }
    
    static of(value) {
      return new Maybe(value);
    }
    }
    
    // let result = Maybe.of(null).map((x) => x + 1);
    // console.log(result);
    
    let result = Maybe.of(10).map((x) => x + 1);
    console.log(result._value);

    Either

    Can be used for exception handling

    class Left {
    constructor(value) {
      this._value = value;
    }
    
    map(fn) {
      return this;
    }
    
    static of(value) {
      return new Left(value);
    }
    }
    
    class Right {
    constructor(value) {
      this._value = value;
    }
    
    map(fn) {
      return Right.of(fn(this._value));
    }
    
    static of(value) {
      return new Right(value);
    }
    }
    
    function parseJson(value) {
    try {
      return Right.of(JSON.parse(value));
    } catch (e) {
      return Left.of({ error: e.message });
    }
    }
    
    let result = parseJson('{ "key": "value" }').map((x) => x.key.toUpperCase());
    console.log(result);
    

    IO

    The internally encapsulated value is a function that encapsulates impure operations into this function, and the impure operations are handled by the caller

    const fp = require("lodash/fp");
    
    class IO {
    constructor(fn) {
      this._value = fn;
    }
    
    map(fn) {
      return new IO(fp.flowRight(fn, this._value));
    }
    
    static of(x) {
      return new IO(() => x);
    }
    }
    let path = IO.of(process).map((p) => p.execPath);
    console.log(path._value());

    Task asynchronous execution

    folktale A standard functional programming library

    const fp = require("lodash/fp");
    const fs = require("fs");
    const { task } = require("folktale/concurrency/task");
    function readFile(filename) {
    return task((resolve) => {
      fs.readFile(filename, "utf-8", (err, data) => {
        if (err) {
          resolve.reject(err);
        }
        resolve.resolve(data);
      });
    });
    }
    
    readFile("package.json")
    .run()
    .listen({
      onRejected: (err) => {
        console.log(err);
      },
      onResolved: (data) => {
        console.log(data);
      },
    });

    Monad (list)

    Avoid functor nesting through join method

    const fs = require("fs");
    const fp = require("lodash/fp");
    
    class IO {
    constructor(fn) {
      this._value = fn;
    }
    
    static of(x) {
      return new IO(() => {
        return x;
      });
    }
    
    map(fn) {
      return new IO(fp.flowRight(fn, this._value));
    }
    
    join() {
      return this._value();
    }
    
    flatMap(fn) {
      return this.map(fn).join();
    }
    }
    let readFile = function (filename) {
    return new IO(() => {
      return fs.readFileSync(filename, "utf-8");
    });
    };
    let print = function (x) {
    return new IO(() => {
      console.log(x);
      return x;
    });
    };
    let result = readFile("package.json").flatMap(fp.toUpper).join();
    console.log(result);
    

Keywords: Javascript Front-end

Added by Rebelrebellious on Tue, 07 Dec 2021 14:10:51 +0200