Why should I learn functional programming

Functional programming is a very old concept, before the first computers, before the history of functional programming

The purpose of learning functional programming:

  • Functional programming is getting more attention with the popularity of React;
  • Vue3 is also embracing functional programming;
  • Functional programming can discard this
  • Tree Shaking can be used to filter out unwanted code in the packaging process
  • Convenient testing, convenient parallel processing
  • There are many libraries that can help us with functional development: Lodash, underscore, Ramda

What is functional programming

Functional Programming (FP), one of the Programming paradigms, often referred to as procedural Programming, object-oriented Programming, Programming paradigms.

  • [Object-oriented programming] thinking mode: abstract the errors in the real world into classes and objects in the program vision, and demonstrate the relationship between transaction events through encapsulation, inheritance and polymorphism (human-Xiaoming)
  • 4. A way of thinking in functional programming: to abstract real-world transactions and their relationships into the programming world (to abstract the computation process).
    • The essence of the program: according to the input through some operation to obtain the corresponding output, the process of program development will involve a lot of input and output functions
    • X -> f -> y, y=f(x)
    • A function in functional programming refers not to a function (or method) in a program, but to a function in mathematics that is a mapping, for example: y=sin(x), the relationship between x and y
    • The same input always gives the same output (pure function)
    • Functional programming describes mapping between data (functions)
// Non-functional
let num1 = 2;
let num2 = 3;
let sum = num1 + num2;
console.log(sum);

/ / function type
function add(n1, n2) {
    return n1 + n2
}
let sum = add(2.3);
console.log(sum);
Copy the code

3. Pre-knowledge

1. Functions are first-class citizens

MDN First-class Function

In JavaScript, a Function is just a normal object (you can use new Function()). We can store the Function in a variable, an array, or as an argument or return value to another Function. We can even construct a new Function with new Function(‘alert(1)’) while the program is running

  • Functions can be stored in variables

  • Function as a parameter

  • Function as the return value

  • Assign a function to a variable

// Assign the function to the variable
let fn = function () {
    console.log('Hello First-class Function')
}
fn()  // Call the function

/ / sample
const BlogController = {
    index(posts) {return views.index(ports)},
    show (post) { return Views.show(post) },
	create (attrs) { return Db.create(attrs) },
	update (post, attrs) { return Db.update(post, attrs) },
	destroy (post) { return Db.destroy(post) }
}
// Example optimization
const BlogController = {
    index: Views.index,
    show: Views.show,
    create: Db.create,
    update: Db.update,
    destroy: Db.destroy
}
Copy the code
  • Function is a first class citizen is later to learn the warning function, function currying basis

2. Higher-order functions

What is a higher order function

  • Higher order Function
    • You can pass a function as an argument to another function
    • You can treat a function as the return result of another function
  • Function as a parameter
/ / simulation forEach
function forEach(array, fn){
    for (let i = 0; i < array.length; i++) { fn(array[i]) } }/ / analog filter
function filter(array, fn) {
    let results = [];
    for (let i = 0; i < array.length; i++){
        if (fn(array[i])) {
            results.push(array[i])
        }
    }
    return results;
}
Copy the code
  • Function as the return value
function makeFn(){
    let msg = 'Hello Function';
    return function () {
        console.log(msg)
    }
}
const fn = makeFn();
fn()
Copy the code
// once Simulates payment. In business scenarios, users are allowed to pay only once
function once(fn){
    let done = false;  // The default value is false
    return function (){
        if(! done){// If not paid, enter the function
            done = true;  // After entering, set done to true to ensure that the function is not entered next time
            return fn.apply(this.arguments)}}}let pay = once(function (money)){
	console.log('Pay:' + money + 'RMB';
}
// Print only once
pay(5);
pay(5);
pay(5);
Copy the code

Apply function prompt

The apply() method calls a function with a given value of this and provides the arguments in the form of an array (or array-like object).

Note: The call() method acts like the apply() method, except that the call() method accepts a list of arguments, while the apply() method accepts an array of arguments.

const numbers = [5.6.2.3.7];

const max = Math.max.apply(null, numbers);

console.log(max);
// expected output: 7

const min = Math.min.apply(null, numbers);

console.log(min);
// expected output: 2
// See MDN for details:
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
Copy the code

Meaning of using higher-order functions

  • Abstractions can help us block out the details (the black box) and focus only on our goals
  • Higher-order functions are used to abstract general purpose problems
// Process-oriented approach
let array = [1.2.3.4];
for (let i = 0; i < array.length; i++) {console.log(array[i])
}

// Higher order function
let array = [1.2.3.4];
forEach(array, item) =>{
    console.log(item)
}

let r = filter(array, item) =>{
    return irem % 2= = =0
}
Copy the code

Commonly used higher order functions

  • forEach
  • map
  • filter
  • every
  • some
  • find/findIndex
  • reduce
  • sort
  • .
/ / the map function
const map = (array, fn) = > {
    let results = []
    for (const value of array) {
    	results.push(fn(value))
    }
    return results
}

/ / every function
const every = (array, fn) = >{
    let result = true;
    for (const value of array) {
        result = fn(value);
        if(! result) {break}}return result
}

/ / some function
const some = (array, fn) = >{
    let result = false;
    for (conse value of array) {
        result = fn(value);
        if (result) {
            break}}return result
}
Copy the code

3. The closure

  • Closure: A function is bundled with a reference to its surrounding state (lexical environment) to form a Closure.

    • You can call the inner function of a function in another scope and access members in the scope of a higher function
    // Function as return value
    function makeFn() {
        let msg = 'Hello Function';
        return function () {
            console.log(msg)
        }
    }
    
    const fn = makeFn();
    fn()
    
    
    // once
    function once(fn) {
        let done = false;
        return function (){
            if(! done) {return fn.apply(this.arguments)}}}let pay = once(function (money) {
        console.log('Pay:' + money + 'RMB')})// Only one payment will be made
    pay(5);
    pay(5);
    Copy the code
  • The nature of closures: functions are placed on an execution stack when they are executed and removed from the stack when they are finished, but the scope members on the heap cannot be freed by external references, so internal functions can still access members of external functions

  • Closure case

// Generate a function that computes a number to a power
function makePower(power) {
    return function (x) {
        return Matn.pow(x, power)
    }
}
let power2 = makePower(2);
let power3 = makePower(3);
console.log(power2(4))
console.log(power3(4))


// The first function is base pay and the second function is performance pay
function makeSalary(x) {
    return function (y) {
        return x + y
    }
}

let salaryLevel1 = makeSalary(1500);
let salaryLevel2 = makeSalary(2000);
console.log(alaryLevel1(2000))
console.log(alaryLevel1(3000))
Copy the code

4. Pure functions

1. Pure functional concepts

  • Pure functions: The same input always yields the same output, without any observable side effects

    • A pure function is like a function in mathematics (which describes the relationship between input and output), y=f(x).
  • Lodash is a pure function library that provides methods for manipulating arrays, numbers, objects, strings, functions, and more

  • The arrays slice and splice are pure and impure functions, respectively

    • Slice returns the specified portion of the array without changing the original array
    • Splice operates on an array and returns that array, and it changes the array
let numbers = [1.2.3.4.5];
/ / pure functions
numbers.slice(0.3)
/ / = > [1, 2, 3]
numbers.slice(0.3)
/ / = > [1, 2, 3]
numbers.slice(0.3)
/ / = > [1, 2, 3]

// Impure function
numbers.splice(0.3)
/ / = > [1, 2, 3]
numbers.splice(0.3)
/ / = > [4, 5)
numbers.splice(0.3)
/ / = > []
Copy the code
  • Functional programming does not preserve the intermediate results of calculation, so variables are immutable (stateless)
  • We can hand over the results of one function to another

2. Benefits of pure functions

  • cacheable

    • Because pure functions always have the same result for the same input, the result of a pure function can be cached
    const _ = require('lodash')
    
    function getArea(r) {
        return Math.PI * r * r
    }
    
    let getAreaWithMemory = _.memoize(getArea)
    console.log(getAreaWithMemory(4))
    Copy the code
  • Simulate a memoize function yourself

function getArea (r) {
  console.log(r)
  return Math.PI * r * r
}

function memoize(f) {
    // Cache the result with an object whose key is the argument passed to f and whose value is the return value passed to f
    let cache = {}
    // Return a function in which to determine if the return value needs to be executed again
    return function () {
        // Arg_str is the argument passed to function f, which is converted to a string using json.stringify
        let arg_str = JSON.stringify(arguments)
        // Assign to cache[arg_str] in two cases:
        // 1. If the key arg_str exists in the cache, assign a value to it
        // 2. There is no arg_str key in the cache, so you need to call the passed f function
        // Call f with f.ply. The first argument refers to f itself, and the second argument is the argument passed to f, followed by its return value
        cache[arg_str] = cache[arg_str] || f.apply(f, arguments)
        return cache[arg_str]
    }
}

/ / 4
/ / 50.26548245743669
/ / 50.26548245743669
/ / 50.26548245743669
Copy the code
  • testable
    • Pure functions make testing easier
  • Parallel processing
    • Working in parallel with shared memory data in a multi-threaded environment is likely to lead to unexpected situations
    • Pure functions do not need to access shared memory data, so they can run as many pure functions as they want in a parallel environment (Web Work).

3. The side effects

  • Pure functions: Always get the same output for the same input, without any observable side effects
/ / not pure
let mini = 18

function checkAge(age) {
    return age >= mini
}

// A pure function (hard code the problem, and then collize it)
function checkAge2(age) {
    let mini = 18
    return age >= mini
}
Copy the code

The side effect of making a function impure (as shown above) is that a pure function returns the same output based on the same input, which can be a side effect if the function is dependent on the external state and cannot guarantee the same output

Source of side effects:

  • The configuration file
  • The database
  • Gets the user’s input
  • .

All external interactions may bring side effects, which also reduce the universality of methods and make them unsuitable for extension and reusability. Meanwhile, side effects will bring security risks and uncertainty to programs, but side effects cannot be completely prohibited, so they should be controlled as much as possible.

5. Currying

  • Use Currying to solve the hard-coded problem in the previous case
/ / not pure
function checkAge(age) {
    let mini = 18
    return age >= mini
}

// Plain pure function
function checkAge(min,age) {
    return age >= mini
}

checkAge(18.24)
checkAge(18.20)
checkAge(20.30)

/ / curry
function checkAge(min) {
    return function (age) {
        return age >= min
    }
}

/ / ES6 writing
let checkAge = min= > (age= > age >= min)

let checkAge18 = checkAge(18)
let checkAge20 = checkAge(20)

checkAge18(24)
checkAge18(20)
Copy the code
  • Currie,
    • When a function has more than one argument, it is called by passing some of the arguments (which never change).
    • It then returns a new function that takes the remaining arguments and returns the result

1. Curryized functions in Lodash

  • _.curry(func)
    • Function: Create a function that takes one or more func arguments, executes func and returns the result if all the arguments required by func are provided. Otherwise, continue returning the function and wait to receive the remaining arguments.
    • Parameters: Functions that require curryization
    • Return value: The curryized function
const _ = require('lodash');

// The function to be Curryized
function getSum(a, b, c) {
    return a + b + c
}

// The curryized function
let curried = _.curry(getSum)
/ / test
curried(1.2.3)
curried(1.2) (3)
curried(1) (2.3)
Copy the code
  • case
const _ = require('lodash')
const match = _.curry(function (reg, str) {
    return str.match(reg)
})

const haveSpace = match(/\s+/g)
const haveNumber = match(/\d+/g)

console.log(haveSpace('Hello World'))
console.log(haveNumber('$25'))

const filter = _.curry(function (func, array) {
    return array.filter(func)
})

console.log(filter(haveSpace, ['John Connor'.'John_Donne']))

const findSpace = filter(haveSpace)
console.log(findSpace(['John Connor'.'John Donne']))
Copy the code
  • Simulate the implementation of _.curry()
function curry(func) {
    return function curriedFn(. args) {
        // Determine the number of arguments and parameters. Func. length represents the length of the parameter of func
        if (args.length < func.length) {
            return function () {
                // Concatenate passed arguments with unpassed arguments. Concat is used for array concatenation
                returncurriedFn(... args.concat(Array.from(arguments)))}}returnfunc(... args) } }Copy the code

2. To summarize

  • Coreization allows us to pass fewer arguments to a function and get a new function that has some fixed arguments memorized
  • This is a kind of caching of function arguments
  • Make functions more flexible and less granular
  • Functions of several variables can be converted into functions of one variable, and functions can be combined to produce powerful functions

Function combination

  • Pure functions and Currying is easy to write onion code (h(g(f(x))))

    • Take the last element of the array and convert it to uppercase,.toUpper(.first(.reverse(array)))
  • Function composition allows us to regroup fine-grained functions to produce a new function

1. The pipe

The following graph shows the process of using a function to process data in a program. Input a to fn and return b as a result. You can imagine that data A goes through a pipe to data B.

When fn is more complicated, we can break it up into smaller functions, with more m and n generated by the intermediate operations.

In the figure below, you can imagine that the pipe FN is divided into three pipes F1, F2 and F3. Data A goes through the pipe F3 to get the result M, m goes through the pipe F2 to get the result N, and n goes through the pipe F1 to get the final result B.

fn = compose(f1,f2,f3)
b = fn(a)
Copy the code

2. Function combination

  • Compose: For example, if a function has to be processed by more than one function for its final value, you can compose the intermediate functions into a single function
    • Functions are like conduits of data, and function combination is the process of connecting these conduits, allowing data to flow through multiple conduits to form the final result
    • Function combinations are executed from right to left by default
// The array of composite functions is flipped before removing the first element
function compose(f, g) {
    return function (x) {
        return f(g(x))
    }
}

function first(arr) {
    return arr[0]}function reverse(arr) {
    return arr.reverse()
}

// Execute from right to left
let last = compose(first, reverse)
console.log(last([1.2.3.4]))
Copy the code
  • Combinatorial functions in lodash
  • Lodash combines the flow() or flowRight() functions, both of which can combine multiple functions
  • Flow () runs from left to right
  • FlowRight () runs from right to left, using a little more
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'])
Copy the code
  • Simulate implementing lodash’s flowRight method
// Multifunction combination
function compose(. fns) {
    // Pass in an indefinite number of functions, using... FNS instead, remaining parameters
    // Return a function that takes a value as an argument
    return function (value) {
        // Return the call from... The last function in FNS is called, so the argument FNS needs to be flipped first
        // Each function needs to process the value and return the processed value
        // Reduce executes the provided function for each element in the array and aggregates it into a single result
        // Reduce is a function that takes two arguments, acc: the cumulative result, fn how to process each result and return a new result
        return fns.reverse().reduce(function (acc, fn) {
            // fn for each function in the array, acc last returned the result
            return fn(acc)
            // acc starts with the value passed in the first call
        }, value)
    }
}

// ES6
const compose = (. fns) = > value= > fns.reverse().reduce((acc, fn) = > fn(acc), value)
Copy the code
  • The combination of functions has to satisfyAssociative law(associativity)
    • We can combine g with h, or we can combine f with g, and the result is the same, that is, the order of combination doesn’t matter
// associativity
let f = compose(f, g, h)
let associative = compose(compose(f, g), h) == compose(f, compose(g, h))
// true
Copy the code
  • So the code could look something like this
const _ = require('lodash')
// const f = _.flowRight(_.toUpper, _.first, _.reverse)
// const f = _.flowRight(_.flowRight(_.toUpper, _.first), _.reverse)
const f = _.flowRight(_.toUpper, _.flowRight(_.first, _.reverse))
console.log(f(['one'.'two'.'three']))
// => THREE
Copy the code

3. The debugging

  • How do I debug composite functions
const f = _.flowRight(_.toUpper, _.first, _.reverse)
console.log(f(['one'.'two'.'three']))
Copy the code
const _ = require('lodash')
    const trace = _.curry((tag, v) = > {
    console.log(tag, v)
    return v
})
const split = _.curry((sep, str) = > _.split(str, sep))
const join = _.curry((sep, array) = > _.join(array, sep))
const map = _.curry((fn, array) = > _.map(array, fn))
const f = _.flowRight(join(The '-'), trace('after the map'), map(_.toLower),
trace('after the split), split(' '))
console.log(f('NEVER SAY DIE'))
Copy the code
  • lodash/fp
    • Lodash’s FP module provides a functional programme-friendly approach to use
    • Provides an immutable auto-curried-first data-last method
/ / lodash module
const _ = require('lodash')
_.map(['a'.'b'.'c'], _.toUpper)
// => ['A', 'B', 'C']
_.map(['a'.'b'.'c'])
// => ['a', 'b', 'c']
_.split('Hello World'.' ')
/ / lodash/fp module
const fp = require('lodash/fp')
fp.map(fp.toUpper, ['a'.'b'.'c'])
fp.map(fp.toUpper)(['a'.'b'.'c'])
fp.split(' '.'Hello World')
fp.split(' ') ('Hello World')
Copy the code
const fp = require('lodash/fp')
const f = fp.flowRight(fp.join(The '-'), fp.map(_.toLower), fp.split(' '))
console.log(f('NEVER SAY DIE'))
// never-say-die
Copy the code

4.Point Free

Point Free: We can define the process of data processing as a data-independent synthesis operation that does not need to use the parameter representing the data

Numbers, as long as the simple operation steps are put together, we need to define some auxiliary basic operation functions before we can use this pattern.

  • You do not need to specify the data being processed
  • You just have to synthesize it
  • Some auxiliary basic operations need to be defined
const f = fp.flowRight(fp.join(The '-'), fp.map(_.toLower), 1 fp.split(' '))
Copy the code
  • Case presentation
// Non-point Free mode
// Hello World => hello_world
function f (word) {
	return word.toLowerCase().replace(/\s+/g.'_');
}
// Point Free
const fp = require('lodash/fp')
const f = fp.flowRight(fp.replace(/\s+/g.'_'), fp.toLower)
console.log(f('Hello World'))
Copy the code
  • Use the Point Free pattern to extract and capitalize the first letter in the word
const fp = require('lodash/fp')
const firstLetterToUpper = fp.flowRight(join('. '),
fp.map(fp.flowRight(fp.first, fp.toUpper)), split(' '))
console.log(firstLetterToUpper('world wild web'))
// => W. W. W
Copy the code

7. Functor

1. Why learn functors

So far, you’ve learned some of the basics of functional programming, but we haven’t shown you how to keep side effects under control, handle exceptions, do asynchronous operations, and so on in functional programming.

2. What is Functor

  • Container: Contains values and their morphing relationships (the morphing relationships are functions)
  • Functor: a special container implemented by a common object that has a map method that runs a function that deals with values (morphing relations)

3. The Functor Functor

// ES6 introduced the concept of class, but js does not have a real class primitive type, just using syntax sugar for prototype objects, so
// If you understand how to use prototyped objects to implement classes and inheritance, you can really use them
class Container {
    // Implement the static method of, instead of calling new Container each time to create the object
    static of(value) {
        return new Container(value)
    }

    // Create a constructor that contains the passed value inside the container.
    constructor(value) {
        Variables starting with // _ are generally defined as internal attributes
        // In general, an underscore before a variable means "private variable." An underscore before a function name means "private function."
        this._value = value
    }

    // Throw the map function to the outside to receive the method that handles the value
    map(fn) {
        return Container.of(fn(this._value))
    }
}

/ / test
let r = Container.of(3)
    .map(x= > x + 2)
    .map(x= > x * x)
console.log(r)
// ==>Container { _value: 25 }
Copy the code
  • conclusion
    • The operations of functional programming do not operate directly on values, but are performed by functors
    • A functor is an object that implements the Map contract
    • We can think of a functor as a box that encloses a value, which is not disclosed to the public
    • To process the value in the box, we need to pass a processing value function (pure function) to the box’s map method, which will process the value
    • Finally, the map method returns a box containing the new value (functor). Calling the map method multiple times will result in a nesting of functors
  • In Functor if you pass a null value (side effect)
Container.of(null)
    .map(x= > x.toUpperCase())
// TypeError: cannot read property 'toUpperCase' of null
Copy the code

4. MayBe functor

  • We may encounter a lot of errors in the process of programming, we need to do the corresponding processing of these errors
  • The MayBe function allows you to handle the external null case (control side effects to the permissible extent)
class MayBe {
    static of(value) {
        return new MayBe(value)
    }

    constructor(value) {
        this._value = value
    }

    isNothing() {
        return this._value === null || this._value === undefined
    }

    // Return null functor if null is distorted
    map(fn) {
        return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this._value))
    }
}

// Pass in the concrete value
let r1 = MayBe.of('Hello world')
    .map(x= > x.toUpperCase())
console.log(r1)  // MayBe { _value: 'HELLO WORLD' }
// Pass null
let r2 = MayBe.of(null)
    .map(x= > x.toUpperCase())
console.log(r2)  // MayBe { _value: null }
Copy the code
  • In the MayBe functor, it is difficult to determine which step caused the null value problem, as shown in the following example:
MayBe.of('hello world')
    .map(x= > x.toUpperCase())
    .map(x= > null)
    .map(x= > x.split(' '))
// => MayBe { _value: null }
Copy the code

5. Either functor

  • Either of two, similar to if… else… To deal with
  • Exceptions make functions impure, and Either functors can be used for exception handling
class Left {
    static of(value) {
        return new Left(value)
    }

    constructor(value) {
        this._value = value
    }

    map(fn) {
        return this}}class Right {
    static of(value) {
        return new Right(value)
    }

    constructor(value) {
        this._value = value
    }

    map(fn) {
        return Right.of(fn(this._value))
    }
}
Copy the code
  • Either is used to handle exceptions
function parseJSON(json) {
    try {
        return Right.of(JSON.parse(json));
    } catch (e) {
        return Left.of({error: e.message}); }}let r = parseJSON('{ "name": "zs" }')
    .map(x= > x.name.toUpperCase())
console.log(r)  // Right { _value: 'ZS' }
Copy the code

6. IO functor

  • The _value in the IO functor is a function, and here we’re treating the function as a value
  • The IO functor can store the impure action in _value, delaying the impure operation (lazy execution), wrapping the current operation
  • Leave impure operations to the caller
const fs = require('lodash/fp')

class IO {
    static of(x) {
        return new IO(function () {
            return x
        })
    }

    constructor(fn) {
        this._value = fn
    }

    map(fn) {
        // Combine the current value with the passed fn function to form a new function
        return new IO(fp.flowRight(fn, this._value))
    }
}

/ / call
let io = IO.of(process)
    .map(p= > p.execPath)

console.log(io._value)  //
Copy the code

7. Execute tasks asynchronously

  • The implementation of asynchronous tasks is too complex, so we use tasks from Folktale to demonstrate
  • Folktale is a standard function programming library
    • Unlike Lodash and Ramda, it doesn’t provide a lot of functionality
    • For example, compose, curry, etc., and some functors such as Task, Either, MayBe, etc
const {compose, curry} = require('folktale/core/lambda')
const {toUpper, first} = require('lodash/fp')

// The first argument is the number of arguments passed to the function
let f = curry(2.function (x, y) {
    console.log(x + y)
})
f(3.4)
f(3) (4)
/ / 7
/ / 7

// Function combination
let r = compose(toUpper, first)
f(['one'.'two'])
console.log(r)
// [function]
Copy the code
  • Task Asynchronous execution
    • The Task in folktale(2.3.2) 2.x is quite different from the Task in 1.0. The use in 1.0 is much closer to the functor we demonstrate now
    • This is illustrated by 2.3.2
const {task} = require('folktale/concurrency/task')
const fs = require('fs')
const {split, find} = require('lodash/fp')

function readFile(filename) {
    return task(resolver= > {
        fs.readFile(filename, 'utf-8'.(err, data) = > {
            if (err) resolver.reject(err)
            resolver.resolve(data)
        })
    })
}

// Call run to execute
readFile('package.json')
    .map(split('\n'))
    .map(find(x= > x.includes('version')))
    .run().listen({
    onRejected: err= > {
        console.log(err)
    },
    onResolved: value= > {
        console.log(value)
    }
})
Copy the code

8. Pointed functor

  • A margin functor is a functor that implements the static method of
  • The “of” method is used to avoid using new to create the object. The “of” method is used to put the value into the Context (put the value into the container, use the map to process the value).
class Container {    static of (value) {        return newThe Container (value)}... }Contanier.of(2)    .map(x= > x + 5)
Copy the code

9.Monad

When using the IO functor, if the following code appears:

const fs = require('fs')
const fp = require('lodash/fp')

class IO {
    static of(x) {
        return new IO(function () {
            return x
        })
    }

    constructor(fn) {
        this._value = fn
    }

    map(fn) {
        // Combine the current value with the passed fn function to form a new function
        return new IO(fp.flowRight(fn, this._value))
    }
}

let readFile = function (filename) {
    return new IO(function () {
        return fs.readFileSync(filename, 'utf-8')})}let print = function (x) {
    return new IO(function () {
        console.log(x)
        return x
    })
}


let cat = fp.flowRight(print, readFile)
/ / call
let r = cat('package.json')._value()._value()
console.log(r)
Copy the code
  • The Monad functor can become a margin functor, IO(IO(x))
  • A functor is a Monad if it has join and of methods and obeies some laws
// IO Monad
class IO {
    static of(x) {
        return new IO(function () {
            return x
        })
    }

    constructor(fn) {
        this._value = fn
    }

    map(fn) {
        return new IO(fp.flowRight(fn, this._value))
    }

    join() {
        return this._value()
    }

    flatMap(fn) {
        return this.map(fn).join()
    }
}

let r = readFile('package.json')
    .map(fp.toUpper)
    .flatMap(print)
    .join()
Copy the code

Eight, the appendix

  • Functional programming refers to north

  • Introduction to functional programming

  • Pointfree Programming style Guide

  • The illustration Monad

  • Functor,Applicatives,And Monads In Pictures