函子

102 阅读3分钟

函子是一个特殊的容器,通过一个普通的对象来实现,该对象具有map方法,map方法可以运行一个函数对值进行处理,这个值永远不对外开放

在函数式编程中如何把副作用控制在可控的范围内,异常处理,异步操作等

// Point函子是实现了of静态方法的函子
// 含义:of方法用来把值放到上下文Context中用map处理
    static of(value) {//不用使用new来创建对象
        return new Container(value)
    }
    constructor(value){//不对外开放
        this._value = value
    }

    map (fn) {//在map中处理value
        // return new Container(fn(this._value))
        return this.isNothing() ? Container.of(null) : Container.of(fn(this._value))
    }

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

 let r = new Container(5).map(v => v + 1)
                 .map(v => v * v)

let rr = Container.of(5).map( v => v + 2)
                        .map( v => v * v )
let err = Container.of(null).map( v => v.split(' '))
console.log(err)//返回Container对象 -新的函子对象


// 异常
// let err = Container.of(null).map( x => x.split(' ') )

二、maybe函子

作用是可以对外部的空值情况做处理(控制副作用在允许的范围)

    constructor(value){
        this._value = value
    }
    static of (value){
        return new MayBe(value)
    }

    map(fn){
        return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this._value))
    }
    isNothing (){
        return this._value === null || this._value === undefined
    }
}

let mb = MayBe.of('hello').map( v => v.toUpperCase())
let err = MayBe.of(null).map( v => v.toUpperCase())
console.log(err)

let nu = MayBe.of('nnn').map( x => x.toUpperCase())
                        .map( x => null)
                        .map( x => x.split(' '))
/

maybe函子可以处理异常 但比如 null 执行中 不知道错误出现在哪里了

三、Either函子

可以用来异常处理 可以记录出错的信息 类似于if...else..

    constructor(value){
        this._value = value
    }
    static of(value){
        return new Left(value)
    }
    map(fn){
        return this
    }
}

class Right{
    constructor(value){
        this._value = value
    }
    static of(value){
        return new Right(value)
    }
    map(fn){
        return Right.of(fn(this._value))
    }
}

// let r1 = Right.of(2).map( x => x + 2) //2
// let r2 = Left.of(2).map( x => x + 2)//4
function parseJSON (str){
    try{
        return Right.of(JSON.parse(str))
    } catch(e){
        return Left.of({err : e.message})
    }
}

let r = parseJSON('{name: a}')//错误写法
console.log(r)

四、IO函子

IO函子中的_value始终是一个函数,把函数当作值来处理

IO函子可以把不纯的动作存储到_value中共,延迟执行这个不纯的操作(惰性执行),保证当前的操作使纯的,把不纯的操作交给调用者来处理


class IO {
    static of(value){//of Point函子(实现了of静态方法的函子)
        return new IO(function () {return value})
    }
    constructor(fn){
        this._value = fn //包装的函数
        // this._value->function(){return process}
    }
    map(fn){//当前函子中的value(函数)和传入的fn组合 
        return new IO(fp.flowRight(fn, this._value))
    }
}
let r = IO.of(process).map(p => p.execPath)
console.log(r._value())//r是纯的 不纯的可能存在于r调用过程中

五、Monad函子

解决函子嵌套问题

具有静态的IO和join方法的函子

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

class IO {
    static of(value){
        return new IO(function(){
            return value
        })
    }
    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 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 r = readFile('../package-lock.json')
            .map(x = x.toUpperCase())//fp.toUpper
            .flatMap(print)
            .join()

六、Task函子

处理异步任务

 const { split, find } = require('lodash/fp')//柯里化以后的函数
 const fs = require('fs')
 function readFile(filename){
     return task(resolver => {
        fs.readFile(filename, 'utf-8', (err, data) => {
            if(err) resolver.reject(err)
            resolver.resolve(data)
        })
     })
 }

 readFile('../package.json')//返回一个Task函子
    .map(split('\n'))
    .map(find(x => x.includes('version')))
    .run()//开始去读取文件
    .listen({//监听事件状态
        onRejected: err => {
            console.log(err)
        },
        onResolved: value => {
            console.log(value)
        }
    })