函数式编程七: IO,task,Pointed,monad函子的学习

IO函子

IO函子中的_value是一个函数,这里把函数作为值来处理。
IO函子可以把不纯的动作存储到_ value中,延迟执行这个不纯的操作(惰性执行),包装当前的操作纯。
把不纯的操作交给调用者来处理。

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

这个of方法接收的是一个数据,在of方法的里面再去返回一个io函子,调用io的构造函数传递一个函数,这个函数其实把传过来的值x包裹起来了,通过of可以看出,io函数最终还是想把这个值给返回,io函子的value保存的是一个函数,而这个函数返回的是一个值,把求值的过程做了延迟处理,想用这个值的时候再调 用io函子中的value这个函数。
io函子中的map方法,map方法还是接收一个fn函数,在map方法里通过调用io的构造函数来创建一个io的函子。

let r = IO.of(process).map((p) => p.execPath);
console.log(r);

函数式编程七:  IO,task,Pointed,monad函子的学习
结果返回的是一个io函子,io函子的value保存的是一个function,现在这个函数还没有被执行,可以直接执行r.value()。打印出当前执行node的路径。、
总结一下,io函子内部帮我们包装了一些函数,当我们在传递函数的时候有可能这个函数是一个不纯的函数,那我们不管这个函数是不是纯函数,io函子在执行的过程中,返回的结果始终是一个纯的操作。

Folktale异步执行

异步任务的实现过于复杂,可以用folktale中的Task来演示。
folktale一个标准的函数式编程库

和lodash,ramda不同的是,他没有提供很多功能函数。
只提供了一些函数式处理的操作,例如:compose,curry等,一些函子Task\Either\MayBe等

先安装folktale这个库npm i folktale.

const { compose, curry } = require("folktale/core/lambda");
const { toUpper, first } = require("lodash/fp");
let f = compose(toUpper, first);
console.log(f(["one", "two"]));

函数式编程七:  IO,task,Pointed,monad函子的学习
Folktale这个库中的task函子来处理异步任务,用2.x版本的folktale来演示一个读取文件的例子。fs是读取文件

//Task处理异步任务
const fs = require("fs");
const { task } = require("folktale/concurrency/task");
function readFile(filename) {
return task((resolver) => {
fs.readFile(filename, "utf-8", (err, data) => {
if (err) {
resolver.reject(err);
} else {
resolver.resolve(data);
}
});
});
}

调用一下上面的函数。

readFile("package-lock.json")
.run()
.listen({
onRejected: (err) => {
console.log(err);
},
onResolved: (value) => {
console.log(value);
},
});

函数式编程七:  IO,task,Pointed,monad函子的学习
读取到了package-lock.json文件,如果想把lockfileVersion这个值解析出来,在onResolved我们拿到了这个value,如果直接在这里处理value的话会特别的麻烦,也不是函数式编程了,因为我们在readFile的时候,它返回的是一个task函子,而所有的函子都有一个map方法,所以在run之前可以调用一下这个函子的map方法,在这个方法里可以处理拿到的结果。
Package.json里其他就是一行一行的内容,可以先用换行对文本进行切割得到一个数组,然后再去寻找数组中带有lockfileVersion的这个数组。

const { split, find } = require("lodash/fp");

readFile("package-lock.json")
.map(split("\n"))
.map(find((x) => x.includes("lockfileVersion")))
.run()
.listen({
onRejected: (err) => {
console.log(err);
},
onResolved: (value) => {
console.log(value);
},
});

函数式编程七:  IO,task,Pointed,monad函子的学习
Pointed函子,一点不陌生,因为我们一直在使用

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

class Container {
static of(value) {
return new Container(value);
}
// ......
}
Container.of(2).map((x) => x + 5);

接下去再学习最后一个函子,monad函子(单子),单细胞动物的意思
在使用IO函子的时候,如果之前写过如下代码

const fp = require("lodash/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));
}
}

我们先来看一个io函子的一个问题
在Linux5有一个cat命令,作用是读取文件的内容,并且把这个内容打印出来,写一个函数来模拟这个命令,先写一个读取文件的函数,再写一个打印的函数,组合成一个cat函数。

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 cat = fp.flowRight(print, readFile);
//IO(IO(x))
let r = cat("package-lock.json")._value()._value();
console.log(r);

最外面的IO是print返回的函子,最里面的io是readFile返回的函子。
函数式编程七:  IO,task,Pointed,monad函子的学习
这里面有一个问题,我们在嵌套函子的函数的时候非常的不方便,如果函子有嵌套的话,我们想要嵌套函子中的函数,需要._value()再.value(),虽然这样也可以去实现,但是这种api的风格看起来一般,可以改造一下。

Monad函子是可以变扁的PointedPointed函子,IO(IO(x)).
一个函子如果具有join和 of两个方法并遵守一些定律就是一个 Monad.

把这个IO类改造成Monad函子,join()方法用来返回value结果,经常会把map和join联合起来使用。flatMap()的作用就是同时去调用map和join,上面的例子改造如下。

const fp = require("lodash/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();
}
}

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 cat = fp.flowRight(print, readFile);
// //IO(IO(x))
// let r = cat("package-lock.json")._value()._value();
let r = readFile("package-lock.json").flatMap(print).join();
console.log(r);

再来复习一下monad函数,就是具有一个静态的io方法,并且具有一个join方法的函子,什么时候使用monad?当一个函数返回一个函子的时候,就可以使用monad来解决嵌套的问题,合并一个函数并且这个函数返回一个值,这时候可以调 用map方法,当我们想要合并一个函数,但是这个函数返回一个函子,这时可以用flatMap方法。

原创:https://www.panoramacn.com
源码网提供WordPress源码,帝国CMS源码discuz源码,微信小程序,小说源码,杰奇源码,thinkphp源码,ecshop模板源码,微擎模板源码,dede源码,织梦源码等。

专业搭建小说网站,小说程序,杰奇系列,微信小说系列,app系列小说

函数式编程七:  IO,task,Pointed,monad函子的学习

免责声明,若由于商用引起版权纠纷,一切责任均由使用者承担。

您必须遵守我们的协议,如果您下载了该资源行为将被视为对《免责声明》全部内容的认可-> 联系客服 投诉资源
www.panoramacn.com资源全部来自互联网收集,仅供用于学习和交流,请勿用于商业用途。如有侵权、不妥之处,请联系站长并出示版权证明以便删除。 敬请谅解! 侵权删帖/违法举报/投稿等事物联系邮箱:2640602276@qq.com
未经允许不得转载:书荒源码源码网每日更新网站源码模板! » 函数式编程七: IO,task,Pointed,monad函子的学习
关注我们小说电影免费看
关注我们,获取更多的全网素材资源,有趣有料!
120000+人已关注
分享到:
赞(0) 打赏

评论抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

您的打赏就是我分享的动力!

支付宝扫一扫打赏

微信扫一扫打赏