函子 monad的使用

2020-12-02  本文已影响0人  笨鸟先飞不

什么是函子 Functor

是一个特殊容器,通过普通对象来实现,该对象具有map方法,map方法可以运行一个函数对值进行处理。(包含of静态方法的函子叫Pointed函子,使用也很广泛)

在函数式编程中的使用

函数式编程不直接操作值,由函子来完成
函子就是一个实现来map契约的对象
我们可以把函子想象成一个盒子,盒子里封装里一个值
想要处理盒子中的值,我们需要给map方法传递一个处理值的函数(纯函数),由这个函数对值进行处理
最终map方法返回一个包含新值的盒子(函子)

普通函子传值异常及解决思路

class 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;
    }
}

// 正常传值及调用
// const r = MayBe.of(null).map(x => x.toUpperCase());
// console.log(r);

// 异常传值
// 这里中间会出现null值,虽然结果不会报错,但通过这结果是不知道到底哪一步出错了
const r = MayBe.of('Hello World').map(x => x.toUpperCase()).map(x => null).map(x => x.split(' '));
console.log(r);

// 解决办法
class Left {
    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));
    }
}

function parseJSON (str) {
    try {
        return Right.of(JSON.parse(str));
    } catch (e) {
        return Left.of({"error": e.message})
    }
}
// const r = parseJSON('{name: zs}');
// console.log(r);

const r = parseJSON('{"name": "zs"}');
console.log(r);


// 可通过either函数处理异常记录错误信息

函子嵌套及解决思路

IO 函子:区别与普通函子初始化时传参为函数,而普通函子为普通变量值
const fp = require('lodash/fp');
const fs = require('fs');

// IO函子
class IO {
    constructor (fn) {
        this._value = fn;
    }
    static of (value) {
        return new IO(function () {
            return value
        })
    }
    map (fn) {
        return new IO(fp.flowRight(fn, this._value));
    }
    // monad新增函数
    join () {
        return this._value();
    }
    // monad新增函数
    flatMap (fn) {
        return this.map(fn).join();
    }
}
// 读取文件
function readFile (filename) {
    return new IO(function () {
        return fs.readFileSync(filename, 'utf-8');
    })
}

// 打印文件中内容
function print (value) {
    return new IO(function () {
        return value;
    })
}

// 未使用monad原理时调用
// const cat = fp.flowRight(print, readFile);
// // IO(IO(x)) 形成函子嵌套
// console.log(cat('package.json')._value()._value());

// 优化后使用monad方式调用

// 分析上面调用逻辑
/**
 * 第一步:调用readFile('package.json')得到一个包含读取文件作为回调函数的IO函子
 * 第二步:调用flatMap传入print,将第一步得到的IO函子和print函数组合,并依次执行,执行第一步得到的IO函子的参数fn即读取文件函数返回读取的文件,以此作为参数再执行print函数,
 * 返回一个IO函子,此时返回的是IO(IO(function(){return value;// 返回读取的文件值})),再执行flatMap内部的join,即调用嵌套IO的_value,即得到嵌套IO内部的IO函数,(因为外层IO的_value就是内层
 * IO)
 * 第三步:再单独执行join函数,即调用上一步返回的内层IO的_value值,即调用print函数中的回调函数,返回获取的文件内容
 * 这样做的好处避免函子嵌套调用,将外部函数转换为纯函数,将不纯部分转到函数内部执行,如上面的第二步,就将IO(IO(fn))函子嵌套放在执行内部了
*/

// const r = readFile('package.json') 
//             .flatMap(print)
//             .join();
// console.log(r);

// 再变更需求
// 将所有获得的字符串变为大写
const r = readFile('package.json')
            // .map(x => x.toUpperCase()) //使用数组中的方法
            .map(fp.toUpper)  // 使用lodash/fp模块中的方法
            .flatMap(print)
            .join();



// 总结
// 什么是monad,包含一个静态函子和一个join方法叫做monad
// 什么时候使用monad方法?当一个函数返回一个函子的时候要想到monad方法,主要解决函子嵌套的问题
// 当我们想要合并函数,函数返回一个值,使用map方法,当函数返回一个函子,使用flatMap方法

Task函子
folktale库中Task函子的使用

const { task } = require('folktale/concurrency/task');
const fs = require("fs");
const { split, find } = require('lodash/fp');

function readFile (fileName) {
    // 返回一个Task函子,其他细节使用时参照folktale库文档
    return task(resolver => {
        fs.readFile(fileName, 'utf-8', (err, data) => {
            if (err) resolver.reject(err);
            resolver.resolve(data);
        })
    })
}

const r = readFile('package.json').map(split('\n')).map(find(x => x.includes('version'))).run()
.listen({
    onRejected: err => {
       console.log(err); 
    },
    onResolved: value => {
        console.log(value);
    }
});
// console.log(r);
上一篇下一篇

猜你喜欢

热点阅读