define
Abstract the operation process and describe the mapping between data (functions)
- First class citizens
- Higher-order functions
- closure
Higher-order functions
Abstraction can mask details and abstract away general problems
closure
You can call an inner function of a function from another scope and access its scope members
Essence: Functions are placed on an execution stack at execution time, and removed from the stack when the function completes execution, but the scope member on the heap cannot be freed because it is referenced externally, so the inner function can also access the external member
Pure functions
Concept: The same input always produces the same output. There were no observable side effects
Eg: Slice (pure function)/splice(impure function)
The characteristics of
Functional programming does not preserve the results of calculations so variables are immutable (stateless);
You can pass the result of a function to another function for processing;
Lodash (for pure functions)
The benefits of pure functions
- cacheable
// memory function const _ = require("lodash"); function getArea(r) { console.log(r) return Math.PI * r * r } // let getAreaWithMemory = _.memoize(getArea) function memoize(f) { let cache = {} return function() { let args = JSON.stringify(arguments); cache[args] = cache[args] || f.apply(f, arguments) return cache[args] } } let getAreaWithMemory = memoize(getArea) getAreaWithMemory(4); getAreaWithMemory(4); getAreaWithMemory(4);Copy the code
-
testable
- To assert that
-
Parallel processing
- Pure functions do not need to access shared memory data and can run pure functions at will
Side effects of pure functions
If the function depends on external state, the output is not guaranteed to be the same
Sources of side effects:
-
The global variable
-
The configuration file
-
The database
-
User input
-
.
Corrification in Lodash
_.curry(fn)
- Function: creates a function that takes one or more arguments of func, executes func if all the arguments required by func are provided and returns the result of the execution. Otherwise continue to return the function and wait for the remaining arguments to be received.
- Parameters: functions that require currization
- Return value: The currified function
Eg: Filter empty strings in the array
const _ = require("lodash") const match = _.curry(function (reg, STR) {return str.match(reg)}) const haveSpace = match(/\s+/g) const filter = _. Curry (fn => arrary => arrary.filter(fn)) const findSpace = filter(haveSpace) console.log(findSpace(['hello world', 'helloworld']))Copy the code
implementation
function curry(fn) { return function curriedFn(... Args) {if (args. Length < fn.length) {return function () {return curriedFn(... args.concat(... Array.from(arguments)))}} return fn. Apply (fn, args) // args) } } function getSum(a, b, c) { return a + b + c; } const curried = curry(getSum); console.log(curried(1, 2, 3)); console.log(curried(1)(2)(3)); console.log(curried(1, 2)(3));Copy the code
conclusion
- Currie, Allows us to pass a function parameter are less already remember some fixed parameters of new function \ color {red} {allows us to pass a function parameter are less a already remember some fixed parameters of new function} allows us to pass a function parameter are less an already remember some fixed parameter’s new schedule The number
- Caching of functions
- Functions are more flexible and less granular
- To convert a multivariate function into a unary function, you can combine functions
Function composition
Concept: If a function needs to be processed by multiple functions to get the final value, it is possible to combine the intermediate functions into a single function
Eg: Fetch the last element of the array
// function compose (f, g) { return function (x) { return f(g(x)) } } function first (arr) { return arr[0] } function reverse (arr) { return Arr.reverse ()} // Run let last = compose(first, reverse) console.log(last([1, 2, 3, 4]) from right to left.Copy the code
Combinatorial functions in Lodash
_. FlowRight Executes from right to right
const _ = require("lodash")
const reverse = arr => arr.reverse()
const first = arr => arr[0]
const toUpper = s => s.toUpperCase()
const fn = _.flowRight(toUpper, first, reverse)
console.log(fn(["a","b","c","d"]));
//D
Copy the code
Analog implementation
const _ = require("lodash") const reverse = arr => arr.reverse() const first = arr => arr[0] const toUpper = s => // Reduce (fn,initValue) receives 2 parameters. The first is the iterator function, which processes each element in the array from left to right. Accumulator is an accumulator, currentValue, currentIndex, and Array. // accumulator is the return value from the last call to this function. The first time for the initialValue | | arr [0] / / the value of the currentValue array function is processing. The first time the initialValue | | arr [1] / / currentIndex array function is dealing with the index of the array / / / / array function call initValue the reduce of the optional second parameter, The initial value of the accumulator. If no, the accumulator's first value is currentValue; // const compose = function(... args) { // return function(value) { // return args.reverse().reduce((acc, fn) => { // return fn(acc) // }, Const compose = (const compose = (... args) => value => args.reverse().reduce((acc, fn) => fn(acc), value) const fn = compose(toUpper, first, reverse)Copy the code
Combination of functions satisfies associative law
let f = compose(f, g, h)
let associative = compose(compose(f, g), h) == compose(f, compose(g, h))
Copy the code
Debug combinatorial function
eg:
// NEVER GIVE UP --> never give up
const _ = require("lodash")
const split = _.curry((separator, str) => _.split(str, separator))
// _.toLower
const join = _.curry((separator, arr) => _.join(arr, separator))
const f = _.flowRight(join('-'),_.toLower,split(' '))
console.log(f('NEVER GIVE UP'))
//n-e-v-e-r-,-g-i-v-e-,-u-p
Copy the code
Obviously, the current result is wrong, so how to debug, we need to combine the characteristics of combination functions to deal with, after the function that may execute the error, a special function for printing the result of the stage, and return the value passed in unchanged, as follows:
const trace = _.curry((tag, v) => { console.log(tag, V) return v}) const f = _. FlowRight (join('-'),trace(" toLower "), trace(" split ") ['NEVER', 'GIVE', 'UP'] // execute toLower and execute result: NEVER, GIVE, UPCopy the code
ToLower returns a string, which needs to be modified
const map = _.curry((fn, arr) => _.map(arr, fn))
const f = _.flowRight(join('-'),map(_.toLower),split(' '))
console.log(f('NEVER GIVE UP'))
// never-give-up
Copy the code
So we get the results we want
lodash/fp
- The FP module in LoDash provides a friendly approach to functional programming
- Immutable auto-curried iteratee-first data-last and other methods are provided
eg:
const _ = require('lodash') _.map(['a', 'b', 'c'], _.toUpper) // => ['A', 'B', 'C'] _.map(['a', 'b', 'c']) // => ['a', 'b', 'c'] _. The split (' Hello World ', ') / / lodash/fp module const fp = the require (' lodash/fp) fp. The map (fp) toUpper, [' a ', 'b', 'c']) fp.map(fp.toUpper)(['a', 'b', 'c']) fp.split(' ', 'Hello World') fp.split(' ')('Hello World')Copy the code
The map method in lodash/ FP
Const _ = the require (" lodash ") the console. The log (_. The map ([' 23 ', '8', '10'], parseInt)) / / errors [23, NaN, Console. log(_. Map (['23', '8', '10'], // fp module const fp = require("lodash/fp") console.log(fp.map(parseInt, ['23', '8', '10']))Copy the code
Point Free
Point free is a stylistic means of function composition
- There is no need to specify the data to be processed
- You just need to synthesize the operation
- You need to define some auxiliary basic operation functions
eg:
// point free
const fp = require("lodash/fp")
// const firstLetterToUpper = fp.flowRight( fp.join(". "),fp.map(fp.first),fp.map(fp.toUpper),fp.split(" "))
const firstLetterToUpper = fp.flowRight( fp.join(". "),fp.map(fp.flowRight(fp.first,fp.toUpper)),fp.split(" "))
console.log(firstLetterToUpper("Hello world"))
Copy the code
As you can see, we merged fp.first with fp.toUpper to reduce array traversal
Functor Functor
- Container: Contains values and deformation relationships of values (functions)
- Functor: a special container implemented by an ordinary object that has a map method that runs a function to manipulate values (deformation relationships)
class Container { static of (value) { return new Container(value) } constructor(value) { this._value = value } map(fn) { return Container.of(fn(this._value)) } } let f = Container.of(5).map(x => x + 1).map(x => Math.pow(x, 2)) console.log(f);Copy the code
The container class can be used as a functor, and fn passed in to the map is used to transform the functor. But what if the functor is null undefined? This is where the Maybe functor is used
MayBe functor
Class maybe {static of (value) {return new maybe (value)} constructor(value) {this._value = value} map(fn) { return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this._value)) } isNothing() { return this._value === null || this._value === undefined } } let r = MayBe.of().map(x => x.toUpperCase()) console.log(r)Copy the code
The MayBe functor can solve the problem of input value exceptions, but if map is called multiple times without being able to locate which map phase is the problem, then the Either functor is introduced
Either functor
We implement a Either functor
//either constructor class Container {static of (value) {return new Container(value)} constructor(value) {this._value = value } map(fn) { return Container.of(fn(this._value)) } } class Left extends Container { map(fn) { return this } } class Right extends Container { map(fn) { return Right.of(fn(this._value)) } } function parseJSON(str) { try { return Right.of(JSON.parse(str)) } catch(e) { return Left.of({error: e.message}) } }Copy the code
Let’s call
let r = parseJSON("{key: a}")
// Container { _value: { error: 'Unexpected token k in JSON at position 1' } }
let r = parseJSON('{"key": "a"}')
// Container { _value: { key: 'a' } }
Copy the code
IO functor
- IO functor
_value
Phi is a function, and we’re treating the function as a value - IO functors can store impure operations to
_value
In, the impure operations are deferred (lazy execution) and only pure operations are wrapped - Leave impure operations to the caller
const fp = require("lodash/fp") class IO { static of(x ) { return new IO(() => x) } constructor(fn) { this._value = fn } map(fn) { return new IO(fp.flowRight(fn, this._value)) } } let r = IO.of(process).map(p => p.execPath) console.log(r._value())Copy the code
R._value () finally performs an impure operation
Task asynchronous execution
Folktale is a standard functional programming library
- and
lodash
,ramda
The difference is that there are not many functional functions provided - Only functional processing operations such as:
compose
curry
Such as, functorTask
,Either
,MayBe
Etc.
Here is a simple application: read some qualifying information from a file
const fs = require("fs")
const { task } = require("folktale/concurrency/task")
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)
})
})
}
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
Pointed functor
The -50% functor is a functor that implements the of static method
of
The method is to avoid usingnew
To create objects, and the deeper meaning isof
Method to put values in contextcontext
To put the value into the container, usemap
To process values)
Monad functor
Monad functors are functors that contain static methods of JOIN and of and are mainly used to solve functor nesting problems
implementation
const fs = require('fs') const fp = require('lodash/fp') let readFile = function (filename) { return new IO(function () { return fs.readFileSync(filename, 'utf-8') }) } let print = function (x) { return new IO(function () { return x }) } 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) // When we want to merge a function, FlatMap (print) // flatMap '.join() 'if return value is a functorCopy the code