一 函子 Functor
函子就是个容器,他有个值并且有个map方法,map方法可以运行一个函数对值进行处理;如何把副作用控制在可控的范围内、异常操作、异步操作等可能需要用到函子;
// 函子// class Container {// constructor(value) {// this._value = value;// }// map(fn) {// return new Container(fn(this._value));// }// }// const a = new Container(5)// .map( x => x + 1)// .map( x => x * x);// console.log(a);class Container {static of (value) {return new Container(value)}constructor(value) {this._value = value;}map(fn) {return new Container(fn(this._value));}}console.log(Container.of(5).map(x => x + 1).map( x => x * x))
函数式编程的运算不直接操作值,而是由函子来完成;函子就是一个实现了map契约的对象;我们可以把函子想象成一个盒子,这个盒子里封装了一个值;想要处理函子中的值,我们需要给盒子的map方法传递一个处理值的函数(纯函数),由这个函数对值进行处理;最终map方法返回一个包含新值的盒子;
二 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;}}// console.log(Maybe.of('hello world').map(x => x.toUpperCase()));// 可以处理空值// const r = Maybe.of(null).map(x => x.toUpperCase());// 发生在哪一步不知道const r = Maybe.of(null).map(x => x.toUpperCase()).map(x => null).map(x => x.split(' '));console.log(r)
三 either函子
两者中的一个,类似于if…else…的处理,异常会让函数变的不纯,either函子可以用来做异常处理;
class Container {static of (value) {return new Container(value)}constructor(value) {this._value = value;}map(fn) {return Container.of(fn(this._value));}}class Left extends Container{map(fn) {return this}}class Right extends Container{}function parseJSON (str) {try{return Right.of(JSON.parse(str))} catch(error) {return Left.of({error: error.message})}}// const r1 = Right.of(12)// .map(x => x + 2);// const r2 = Left.of(12)// .map(x => x + 2);// const r3 = parseJSON('{name: zhangSan}')const r4 = parseJSON('{"name": "zhangSan"}').map(x => x.name.toUpperCase())// console.log(r1)// console.log(r2)// console.log(r3)console.log(r4)
四 IO函子
IO函子中的_value是个函数,这里把函数当作值处理;IO函子可以把不纯的动作存储到_value中,延迟执行这个不纯的操作(惰性行为),包装当前的操作;把不纯的操作当作调用者来处理;
const fp = require('loadsh/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))}}// 调用let r = IO.of(process).map( p => p.execPath);// console.log(r);console.log(r._value());
五 folktale
一个标准的函数式编程库,异于lodash、ramda,他没有提供很多功能函数;只提供了函数式的操作,如compose、curry等,一些函子Task、Eithger、Maybe等;
异步任务的实现过于复杂,用folktale中的task来演示
// folktale 中的 curry/compose函数const {compose, curry} = require ('folktale/core/lambda');const {toUpper, first} = require('lodash/fp');// let f = curry(2, (x, y) => x + y);// console.log(f(2,3));// console.log(f(2)(3))const f = compose(toUpper, first);console.log(f(['one', 'two']))
folktale task异步执行 folktale2.和folktal1.中的task区别很大,1.0中的用法更接近我们现在演示的函子
const fs = require('fs');const {task} = require('folktale/concurrency/task');const {split, find} = require('loadsh/fp');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(split('\n')).map(find(x => x.includes('version'))).run().listen({onRejected: err => {console.log(err)},onResolved: data => {console.log(data)}})
六 pointed函子
实现了of静态方法的函子,of方法是为了避免使用new来创建对象,更深层次的含义是of方法用来把值放到上下文context(把值放到容器中,使用map来处理值)
七 io函子的问题
const fp = require('loadsh/fp');const fs = require('fs');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 readFile = function(fileName) {return new IO(function(){return fs.readFileSync(fileName, 'utf-8')});}const print = function(x) {return new IO(function(){console.log(x);return x;})}const cat = fp.flowRight(print, readFile);const r = cat('package.json')._value()._value();console.log(r)
八 monad 函子
可以变扁的poinged函子。变扁就是解决函数嵌套的问题;一个函子如果具有join和of的两个方法并遵守一些定律就是monad;
const fp = require('loadsh/fp');const fs = require('fs');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();}}const readFile = function(fileName) {return new IO(function(){return fs.readFileSync(fileName, 'utf-8')});}const print = function(x) {return new IO(function(){console.log(x);return x;})}const r = readFile('package.json').map(fp.toUpper).flatMap(print).join()console.log(r)
九 函子的练习
support.js
class Container {static of(value) {return new Container(value);}constructor(value) {this._value = value;}map(fn) {return Container.of(fn(this._value));}}class MayBe {static of(x) {return new MayBe(x);}constructor(x) {this._value = x;}isNothing() {return this._value === null || this._value === undefined;}map(fn) {return this.isNothing() ? this : MayBe.of(fn(this._value));}}module.exports = { MayBe, Container };
const fp = require('loadsh/fp');const {MayBe, Container} = require('./support');// 使用fp.add 和 fp.map创建一个能让functor里的值增加的函数let mayBe = MayBe.of([5, 6, 1]);let ex1 = () => {return mayBe.map(fp.map( item => fp.add(item, item)))}console.log(ex1())// 使用一个函数ex2 能够使用fp.first获取列表的第一个元素let xs = Container.of(['do','ray','me','fa','so','la','ti','do']);let ex2 = () => {return xs.map(fp.first)}console.log(ex2())// 实现一个函数ex3, 使用 safeProp 和 fp.first 找到user 名字的首字母let safeProp = fp.curry(function(x, o) {return MayBe.of(o[x])});let user = {id: 2, name: 'Albert'};let ex3 = () => {return safeProp('name', user).map(fp.first);}console.log(ex3())// 使用 Maybe 重写ex4,不要有if语句// let ex4 = function(n) {// if(n) return parseInt(n);// }let ex4 = (n) => {return MayBe.of(n).map(parseInt)}
