Functor (Functor)

What is a Functor

  • Container: contains values and deformation relationships of values (deformation relationships are functions)
  • Functor: a special container implemented by an ordinary object that has a map method that runs a function to manipulate values (deformation relationships)

Functor Functor

// A container containing a value
class Container {
    // of static methods that create objects without the new keyword
    static of(value){
		return new Container(value)
    }
    
    constructor (value) {
        this._value = value;
    }
    
    // The map method, passing in the deformation relation (a function), maps each value in the container to a new container
    map(fn) {
		return Container.of(fn(this._value))
    }
}

/ / test
Container.of(3)
	.map(x= > x + 2)
	.map(x= > x * 3)
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 method
    • We can think of a functor as a box that encapsulates a value
    • To process the values in the box, we need to pass a value-handling function (pure function) to the box’s Map method, which will process the values
    • The final map method returns a new functor of the processed value

Maybe functor

  • During the programming process, you may encounter many errors, which need to be handled accordingly
  • The Maybe functor is used to handle external null cases (to keep side effects within permissible limits).
// A container containing a value
class Container {
    // of static methods that create objects without the new keyword
    static of(value){
        return new Container(value)
    }
    
    constructor (value) {
        this._value = value;
    }
    
    // The map method, passing in the deformation relation (a function), maps each value in the container to a new container
    map(fn) {
        return isNothing() ? Container.of(null) : Container.of(fn(this._value))
    }
    
    isNothing(){
        return this._value === null || this._value === undefined; }}/ / test
Maybe.of('hello world')
    .map(x= > x.toUpperCase())
// When null is passed
Maybe.of(null)
	.map(x= > x.toUpperCase())

// In the Maybe functor, it is difficult to confirm which step causes the null value problem, as follows
Maybe.of('hello world')
    .map(x= > x.toUpperCase())
	.map(x= > null)
	.map(x= > x.split(' '))
Copy the code

Either functor

  • Either of the two, similar to if… else… The processing of
  • Exceptions make functions impure, and Either functors can be used for exception handling
class Left {
    static of(value){
        return new Container(value)
    }
    constructor (value) {
        this._value = value;
    }
    map(fn) {
        return this}}class Right {
    static of(value){
        return new Container(value)
    }
    constructor (value) {
        this._value = value;
    }
    map(fn) {
        return Right.of(fn(this._value))
    }
}

function parseJson(json){
	try{
        return Right.of(JSON.parse(json));
    }catch(e){
        return Left.of({error:e.message})
    }
}

/ / test
let r = parseJSON('{"name":"zs"}')
	.map( x= >{ x.name.toUpper } )
console.log(r);
Copy the code

IO functor

  • The _value in the IO functor is a function, which is treated as a value
  • IO functors can store impure actions in _value, delay execution of the impure operation (lazy execution), and leave the impure operation to the caller
const fp = require('lodash/fp');

class IO {
    static of(value){
        return new Container(function(){
            returnvalue; })}constructor (fn) {
        this._value = fn
    }
    map(fn) {
        // Combine the current value and the passed fn into a new function
        return IO.of(fp.flowRight(fn, this._value))
    }
}

/ / call
let io = IO.of(process).map(p= >{p.execPath})
console.log(io.value())
Copy the code

Task executes functors asynchronously

  • The implementation of asynchronous tasks is too complex and we use Tasks in Folktale to demonstrate this
  • Folktale is a standard functional programming library
    • Unlike Lodash and Ramda, it doesn’t offer a lot of functionality
    • It provides only a few functionally handled operations, such as compose, Curry, etc., some functor tasks, Either, Maybe, etc
const { task } = require('folktable/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);
            resolve.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

Pointed functor

  • The Conservative functor is a functor that implements the of static method
  • The of method is used to avoid using new to create objects. The deeper implication is that the of method is used to put values into Context (putting values into containers and using maps to process values)

Monad functor

When using IO functors, if we write the following code

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

let readFile = function(filename) {
	return new IO(function(){
        return fs.readfileSync(filename,'uft-8')})}let print = function(x){
	return new IO(function(){
        return new IO(function(){
            console.log(x);
            returnx; })})}// IO(IO(x))
let cat = fp.flowRight( print, readFile );

/ / call
let r = cat('package.json')._value()._value();
console.log(r);
Copy the code
  • The Monad functor is a Pointed functor that can be flattened, IO(IO(x))
  • A functor is a Monad functor if it has both 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 = value;
    }
    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