Fundamentals of javascript functional programming

Keywords: Javascript

highlight: a11y-dark
theme: scrolls-light

Functional programming concepts

The essence of the program is to get the output according to the input. X - > F (contact, mapping) - > y, y=f(x).

Function is a first-class citizen, which is an ordinary object. It can be used as a parameter of another function, as a return value of a function, and can also be stored as a variable

//  Non function  
const num1 = 2;  
const num2 = 3;  
const sum = num1 + num2;  
// console.log(sum)

//  Functional expression (function expression, assignment of function as a variable)  

const add = (num1, num2) => { 
    return num1 + num2;  
}  
// console.log(add(num1, num2))

Higher order function

Higher order functions can be used to share common methods (forEach,map,reduce,some,every,find,findIndex)

One of the conditions must be met to meet the higher-order function:

1.The argument to one function can be another function  
2.The return value of a function is another function
//  Function as parameters, as follows: forEach,filter

//  Handwritten forEach function
const forEach = (array, fun) => {
    for (let index = 0; index < array.length; index++) {
        const element = array[index];
        fun(element)
    }
}
forEach([1,2,3,4,5], num => console.log(num * 2))
// 2 4 6 8 10

//  Handwritten filter function
const filter = (array, fun) => {
    const result = [];
    for (let index = 0; index < array.length; index++) {
        const element = array[index];
        if (fun(element)) result.push(element)
    }
    return result; 
}
console.log(filter([1,2,3,4,5], num => num % 2 === 0))
//  [ 2, 4 ]

//  Function as the return value, such as makeFn,once
const makeFn = () => {
    const msg = 'hello function';
    return _ => console.log(msg);
}
makeFn()()
//  hello function

//  Only pay once
const once = fn => {
    let done = false;
    return function() {
        if (!done) {
            done = true;
            fn.apply(this, arguments)
        }
    }
}

const pay = once((money) => {
    console.log(`payment ${money}Yuan money`)
})
pay(5)
pay(5)
pay(5)

Pay 5 yuan

Closure function

To invoke the internal function of a function in another scope and access the member in the scope of the function (externally accessing an internal function of the function, which refers to the member of the function).

The essence of closures: functions are placed on an execution stack when they are executed. After the functions are executed, they are 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

The above makeFn and once use closures. Closures must be a high-order function because they take the function as the return value

/*  For example, to find the n-th power of a number, if I have a large number of values to find the power of 2, we can find a power of 2
 Function, and then pass the required value. Here, the parameter power of the outer function does not change after makePower is executed
 After being destroyed, the inner function will continue to be used, so a closure is formed
*/
const makePower = (power) => num => Math.pow(num, power);

const power2 = makePower(2);
const power3 = makePower(3);

console.log(power2(3))
console.log(power3(3))

Pure function

The same input will always get the same output without any observable side effects (i.e. external state). A pure function is similar to a function in Mathematics (used to describe the relationship between input and output), y = f(x)

//  For impure function, mini is an external configuration attribute. If it is changed, the condition of pure function is not satisfied
const mini = 18;
const checkAge = age => age > mini;

//  Change to pure function
const checkAge = age => {
    const mini = 18;
    return age > age;
}

side effect

Side effects will make your function impure. Side effects cannot be completely prohibited. Try to control them within a controllable range, such as the example of pure functions above.

Sources of side effects:
1. Configuration file
2. Database
3. User input
...

currying

When a function has multiple parameters, you can first pass some parameter calls, and then return a new function to accept the remaining parameters until the parameters are accepted and return the results

It is equivalent to executing a multi parameter function in batches, and we can get the function cache

characteristic:
1. Coriolism allows us to pass fewer parameters to a function to get a new function that has remembered some fixed parameters
2. This is a cache of function parameters
3. Make the function more flexible and make the granularity of the function smaller
4. It can convert multivariate functions into univariate functions, and can combine functions to produce powerful functions

//  Coriolis age function
const checkAge = mini => age => age > mini;
const check18 = checkAge2(18);
console.log(check18(20));
//  true
/*  To realize coriolism, the first parameter is a function. There are N parameters of this function, and the return value is also a function
 Number, when this function is curried, call this function, if the parameter is less than N, then continue to return a function, etc.
After the remaining parameters are passed, if the parameters are equal to N, the final result will be returned directly
*/
const curry = fn => {
    return function curriedFn(...args) {
        // Determine the number of arguments and formal parameters
        if(args.length < fn.length){
            return function() {
                return curriedFn(...args, ...arguments);
            }
        }
        return fn(...args);
    }
}

const ownerCurriedFilter = curry((fn, array) => array.filter(fn))

//  The results of the two methods are the same
console.log(ownerCurriedFilter(num => num % 2 === 0, [1, 2, 3, 4]))
console.log(ownerCurriedFilter(num => num % 2 === 0)([1, 2, 3, 4]))
//  [ 2, 4 ]

Function combination

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

characteristic:
1. Functions are like data pipelines. Function combination is to connect these pipelines and let the data pass through multiple pipelines to form the final result
2. Function combination is executed from right to left by default
3. The combination of functions satisfies the combination law

const reverse = array => array.reverse();
const first = array => array[0];
const toUpper = str => str.toUpperCase();

//  The lodash library is used here
const flowRightFn = _.flowRight(toUpper, first, reverse);
console.log(flowRightFn(['aaa', 'bbb', 'ccc']));
//  CCC
/*  To realize function combination, the first parameter passed is a group of functions, and the return is a function, which is characterized by the previous function
 The return value of the execution is the parameter of the next function, which is passed down in the form of a pipeline until all functions are returned after running
 result
*/
const compose = (...args) => {
    return function(value) {
        return args.reverse().reduce((current, fn) => {
            return fn(current)
        }, value)
    }
}
const flowRightFn = compose(toUpper, first, reverse);
console.log(flowRightFn1(['aaa', 'bbb', 'ccc']));
//  CCC

Lodash is a very practical function library. The functions such as string, array and object processing are pure functions. The functions in fp module are cored and can be used directly. Skilled use of lodash library can improve development efficiency

pointfree pattern is a programming style, which is the combination of functions

characteristic:
1. It is not necessary to specify the data to be processed
2. Only the process of synthesis operation is required
3. Some auxiliary basic operation functions need to be defined

Functor functor

The purpose is to control the side effects, exception handling, asynchronous operation and so on in functional programming. Personal understanding is a box in which a value is stored. This value is invisible to the outside. To obtain this value or operate this value, you need specific execution to get it. After processing this value, you continue to return to a box, which contains your new value.

characteristic:
1. The operation of functional programming is not directly operated on the value, but completed by the functor
2. Functor is realization; Object of map contract
3. We can think of a functor as a box in which a value is encapsulated
4. To process the value in the box, we need to pass a pure function to process the value to the map of the box, which is used to process the value
5. The final map method returns a functor containing the new value

//  A basic functor, a container, wraps a value
class Container {
    //  of static method, you can omit the new keyword to create an object
    static of (value) {
        return new Container(value)
    }

    constructor(value) {
        this._value = value
    }
    //  Map method, pass in a function to map each value in a container to another container
    map(fn) {
        return Container.of(fn(this._value));
    }
}

Container.of(2)
.map(x => x + 2)
.map(x => x * x)

IO functor

characteristic:
1. In io functor Value is a function. Here, the function is treated as a value
2.IO functors can store impure actions in_ In value, delay the execution of the non saved operation to ensure that the current operation is pure
3. Leave the operation that does not exist to the caller for processing

/*  _fn.flowRight It's a combination of functions in lodash, so we want to deal with it_ The value returned by the value function, we
    It can be written anywhere in the call, because the function combination will be processed first_ Function of value
*/
class IO {
    static of() {
        return new IO(function() {
            return x
        })
    }
    constructor(fn) {
        this._value = fn;
    }

    map(fn) {
        //  Combine the current value and the passed in fn into a new function
        return new IO(_fn.flowRight(fn, this._value))
    }
}

Posted by sv4rog on Wed, 13 Oct 2021 15:10:04 -0700