函数式编程(四)函子

一、什么是函子

函子其实就是一个特殊的容器,这个容器中包含值和值的变形关系(函数)。函子可以通过一个普通的对象来实现,该对象具有map方法,map方法可以运行一个函数对值进行处理(变形关系)。

class Container {
    static of(value){
    	// 创建一个静态方法方便函子创建
        return new Container(value)
    }
    // 创建函子的时候,函子内部要有一个值
    constructor(value) {
        this._value = value
    }
    map(fn){
    	// 返回一个新的函子
        return Container.of(fn(this._value))
    }
}
let r = Container.of(5)
        .map(x => x + 2)
        .map(x => x * x)
console.log(r);

二、MayBe函子

在创建函子的时候可能会传入 null 或者 undefined

// 例如:
Container.of(null).map(x => x.toUpperCase()) // 报错了
// 所以我们需要解决值可能为null的情况
// MayBe 函子的作用就是可以对外部的空值情况做处理
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;
    }
}
const s = MayBe.of(undefined)
            .map(x => x.toUpperCase())
console.log(s); // {_value: null}

// 如果在多次调用map方法的时候,某一步使其变成了null,我们是不太明确的
// 创建一个Fail函子专门用来提示异常
class Fail{
    static of(value){
        return new Fail(value)
    }
    constructor(value){
        this._value = value
    }
    map(fn){
        return this;
    }
}
// 将MayBe函子进行修改
class MayBe2{
    static of(value){
        return new MayBe(value)
    }
    constructor(value){
        this._value = value
    }
    map(fn){
        // 如果执行fn后结果为null或者undefined,将fn
        return this.isNothing()? MayBe2.of(null) : (fn(this._value)?MayBe2.of(fn(this._value)):Fail.of(fn.toString()))
    }
    isNothing(){
        return this._value === null || this._value === undefined;
    }
}
const t = MayBe.of('hello world')
            .map(x => x.toUpperCase())
            .map(x => null)
            .map(x => x.split(' '))
console.log(t) // Fail { _value: 'x => null' }

三、Either函子

Either 两者中的任何一个,类似于 if…else…的处理
异常会让函数变的不纯,Either 函子可以用来做异常处理

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))
    }
}
function parseJSON(str){
    try{
        return Right.of(JSON.parse(str))
    } catch(e) {
        return Left.of({error: e.message})
    }
}
// let r = parseJSON('{name:jake}')
// console.log(r);// 打印结果:Left { _value: { error: 'Unexpected token n in JSON at position 1' } }
let r = parseJSON('{"name":"jake"}').map(x => x.name.toUpperCase())
console.log(r);// 打印结果:Right { _value: { name: 'JAKE' } }

四、IO函子

IO函子与其他函子不一样的是,IO函子的值是一个函数,该函数的返回值就是我们传入的参数。

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))
    }
}
const f = IO.of(100)
console.log(f._value()); // 100

IO函子有一些问题需要我们了解以及处理。

// 以下代码是运行在 node 环境下的
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))
    }
}

const obj = {
    name:"憨批",
    age: 18,
    gender: 1
}
// getData函数实现获取obj数据
let getData = function(data){
    return IO.of(data)
}
// getValues函数获取obj的所有属性值
let getValues = function(data){
    console.log("getValues", data);
    // 我的本意是:return IO.of(Object.values(data))
    // 但实际上,getData返回的是一个IO函数,需要执行其中的_value()方法,才能拿到obj数据
    // 如果这样做了就体现不出来IO函子的问题,所以直接返回IO.of(data)
    return IO.of(data)
}
let fn = fp.flowRight(getValues, getData)
let result = fn(obj)
console.log(result._value()._value());// 需要调用两次 _value() 才能打印出数据
// 这就是IO函子嵌套的问题

为了解决IO函子的嵌套问题,我们需要用到monad函子,monad函子其实就是在函子中新增了flatMap和join方法。

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(){ // join的作用就是调用 _value
        return this._value()
    }
    flatMap(fn){ // flatMap的作用就是同时去调用map 和join
        return this.map(fn).join()
    }
}
const obj = {
    name:"憨批",
    age: 18,
    gender: 1
}
// 假设obj是接口返回的数据,一个函数实现获取obj对象数据,一个函数获取obj的所有属性值
let getData = function(data){
    return IO.of(data)
}
let getValues = function(data){
    return IO.of(Object.values(data))
}
let res = getData(obj)
            .flatMap(getValues)// 当我们要合并的这个函数,它返回的值是一个函子,就用flatMap
            .join()
console.log(res); // ['憨批', 18, 1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值