函数式编程(下)

之前是函数式编程的一些基础笔记,以下是我认为比较难理解的一些函数式编程的内容,所以分开单独写一篇笔记。

Functor 函子

  • 函数式编程运算不直接操作值,而是由函子完成
  • 函子具有map方法,map方法可以运行一个函数对值进行处理(变形关系)
  • 把函子比喻成一个盒子,这个盒子里封装了一个值
  • 想要处理盒子中的值,我们需要给盒子的map方法传递一个处理值的函数(纯函数),由这个函数来对值进行处理
  • 最终map方法返回一个包含新值的函子
class Container {
    // of 静态方法,可以省略new关键字创建对象
    static of (value) {
        return new Container(value);
    }
    constructor (value) {
        this._value = value;
    }
    map (fn) {
        return Container.of(fn(this._value));
    }
}

let a = Container.of(3).map(v => v + 3).map(v => v * v);
console.log(a);

MayBe 函子

  • 在编程的过程中可能会遇到很多错误,需要对这些错误做出相应的处理
  • MayBe函子的作用就是可以对外部的空值情况做处理(控制副作用在允许的范围)

class MayBe {
    static of (value) {
        return new MayBe(value);
    }
    constructor (value) {
        this._value = value;
    }
    // 如果对空值变形的话直接返回 值为 null 的函子
    map (fn) {
        return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this._value));
    }
    isNothing () {
        return this._value === null || this._value === undefined;
    }
}

let a = MayBe.of(3).map(v => v + 3);
let b = MayBe.of(null).map(v => v + 3);
console.log(a);
console.log(b);

Either函子

  • Either两者中的任何一个,类似于if…else…的处理
  • Either函子可以用来处理异常
class Left {
    static of (value) {
        return new Left(value);
    }
    constructor (value) {
        this._value = value;
    }
    map (fn) {
        return this._value;
    }
}
class Right {
    static of (value) {
        return new Right(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});
    }
}
let a = parseJson('{"name": "zx"}').map(x => x.name.toUpperCase());
console.log(a);

IO函子

  • IO函子中的_value是一个函数,这里是把函数作为值来处理
  • IO函子可以把不存的动作存储在_value中,延迟执行这个不纯的操作
  • 把不纯的操作交给调用者来处理

(这里比较难理解,我看了好多遍,才大概理解清楚)

const fp = require('lodash/fp');
class IO {
    static of (v) {
        return new IO (() => v);
    }
    constructor (value) {
        this._value = value;
    }
    map (fn) {
        // 把当前的 value 和 传入的 fn 组合成一个新的函数
        return new IO(fp.flowRight(fn, this._value));
    }
}
let io = IO.of(process).map(v => v.execPath);
console.log(io._value());

Task 异步执行

  • 异步任务的实现过于复杂,我们使用 folktale 中的 Task 来演示
  • folktale 一个标准的函数式编程库
    • 和 lodash、ramda 不同的是,他没有提供很多功能函数
    • 只提供了一些函数式处理的操作,例如:compose、curry 等,一些函子 Task、Either、 MayBe 等
const { task } = require('folktale/concurrency/task');
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')
.map(v => v.split('\n'))
.run()
.listen({
    onRejected: err => {
        console.log(err);
    },
    onResolved: value => {
        console.log(value);
    }
})

Pointed 函子

  • Pointed函子是实现of静态方法的函子
  • of方法是为了避免使用new来创建对象,更深层含义是of方法用来把值放到上下文context(把值放在容器中,使用map来处理值)

Monad

为了凸显Monad的作用,对比一下之前在使用IO函子的时候,如果我们写如下代码

const fs = require('fs');
const fp = require('lodash/fp');
class IO {
    static of (v) {
        return new IO (() => v);
    }
    constructor (value) {
        this._value = value;
    }
    map (fn) {
        // 把当前的 value 和 传入的 fn 组合成一个新的函数
        return new IO(fp.flowRight(fn, this._value));
    }
}

let readFile = function (filename) {
    return new IO(function() {
        return fs.readFileSync(filename, 'utf-8');
    })
}
let print = function (a) {
    return new IO(function () {
        console.log(a._value())
        return a;
    })
}
let cat = fp.flowRight(print, readFile);
let b = cat('package.json')._value()._value();
console.log(b);

需要调用多次_value(),很不方便

  • Monad函子是可以变扁的Pointed函子
  • 一个函子如果具有join和of两个方法并遵循一些定律就是一个Moned
const fs = require('fs');
const fp = require('lodash/fp');
class IO {
    static of (v) {
        return new IO (() => v);
    }
    constructor (value) {
        this._value = value;
    }
    map (fn) {
        // 把当前的 value 和 传入的 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 (a) {
    return new IO(function () {
        return a;
    })
}
let b = readFile('package.json').flatMap(print).join();
console.log(b);

总结

函数式编程是一种编程范式或是一种编程思想和面向对象思想其实是同一级别的,他的核心思想是把运算过程抽象成函数。
好啦!以上就是函数式编程的全部笔记啦,其实对于函子的实际应用还有一点模糊,可能是因为之前接触和关注的比较少吧。通过这次的函数式编程的学习,给我的感受是之前的开发都是拿来即用,往原理或者源码上面思考的比较少,今后开发中也会多偏向于原理的思考和源码的解析,这样对于可能会发生的错误才不会那么棘手吧!加油鸭!!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值