函子
- 概念: Functor
- 容器:包含值和值的特殊关系(这个变形关系就是函数)
- 函子:是一个特殊的容器 通过一个普通的对象来实现,该对象具有 map 方法 ,map 方法可以运行一个函数对值进行出(变形关系)
- 函子的作用:处理副作用 异常处理 异步操作等
函子的结构
// 创建一个类
class Contains{
// 构造器
constructor(value){
this._value = value
}
// map方法
map(fn){
return new Contains(fn(this._value))
}
}
let r = new Contains(5).map(x=>x+1).map(x=>x*x)
console.log(r); // 36
- 总结
- 函数式编程的运算不直接操作值,而是由函子来做变形处理
- 函子就是一个实现了map方法的一个容器
- 可以把函子想象成一个盒子,这个盒子里封装了值,想要处理这个值我么需要给map方法传递一个处理值的函数
- 永远不会取出值
在Functor中如果我们传入null或undefined
class Contains{
constructor(value){
this._value = value
}
map(fn){
return new Contains(fn(this._value))
}
}
let r = new Contains(5).map(x=>x+1).map(x=>x*x)
console.log(r); // 36
class Contains{
static of(val){
return new Contains(val)
}
constructor(value){
this._value = value
}
map(fn){
return Contains.of(fn(this._value))
}
}
let r = new Contains(5).map(x=>x+2).map(x=>x*x)
console.log(r); // 49
// 演示 null undefined的问题 如果不小心传入了空值
Contains.of(null).map(x=>x.toUpperCase())
Maybe函子
对函子内部参数错误如传入null或者undefined进行处理,控制副作用在可控范围内
class MayBe{
static of(val){
return new MayBe(val)
}
constructor(val){
this._val = val
}
map(fn){
return this.isNothing()?MayBe.of(null):MayBe.of(fn(this._val))
}
isNothing(){
return this._val === null || this._val === undefined
}
}
let r = MayBe.of('hello lala').map(x=>x.toUpperCase())
// console.log(r);
let r1 = MayBe.of(null).map(x=>x.toUpperCase())
// console.log(r1);
let r2 = MayBe.of('hello lala')
.map(x=>x.toUpperCase())
.map(x=>null)
.map(x=>x.split(' '))
console.log(r2);
Either函子
异常会让函数变得不纯,Either函子可以用来处理异常 类似 if…else…
class Left{
static of(val){
return new Left(val)
}
constructor(val){
this._val = val
}
map(fn){
return this
}
}
class Right{
static of(val){
return new Right(val)
}
constructor(val){
this._val = val
}
map(fn){
return Right.of(fn(this._val))
}
}
let r1 = Right.of(12).map(x=>x+2)
let r2 = Left.of(12).map(x=>x+2)
console.log(r1);
console.log(r2);
function parseJson(str) {
try {
return Right.of(JSON.parse(str))
} catch (e) {
return Left.of({err:e.message})
}
}
let r = parseJson('{"name":"zs"}').map(x=>x.name.toUpperCase())
console.log(r);
IO函子
- 内部存储的是一个函数,把函数作为值来处理
- IO函子把不纯的函数存储起来,让不纯的操作交给调用者进行延迟执行,把副作用控制在可控范围
const fp = require('lodash/fp')
class IO{
static of(val){
return new IO(function(){
return val
})
}
constructor(val){
this._val = val
}
map(fn){
return new IO(fp.flowRight(fn,this._val))
}
}
let r = IO.of(process).map(p=>p.execPath)
console.log(r);
console.log(r._val());
Task函子
- 使用folktale中的Task来演示。
- folktale是一个标准的函数式编程库。
- 只提供了一些函数式处理操作,如compose、curry等 一些函子Task、Either、MayBe等
const {compose,curry} = require('folktale/core/lambda')
const {toUpper,first} = require('lodash/fp')
let f = curry(2,(x,y)=>x+y)
console.log(f(1,2));
console.log(f(1)(2));
let f1 = compose(toUpper,first)
console.log(f1(['one','two']));
task异步执行
const fs = require('fs')
const { task } = require('folktale/concurrency/task')
const {split,find} = require('lodash/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:value=>{
console.log(value);
}
})
Pointed函子
- Pointed函子是实现了of静态方法的函子
- of方法是为了避免使用new来创建对象,更深层的含义是of方法来把值放到上下文Context(把值放到容器中,使用map来处理)
class Contains{
static of(val){
return new Contains(val)
}
...
}
Contains.of(2).map(x=>x+1)
Monad(单子)
- 一个函子如果具有join和of两个方法并遵守一些定律就是一个Monad
const fs = require('fs')
const fp = require('lodash/fp')
class IO{
static of(val){
return new IO(()=>val)
}
constructor(val){
this._val = val
}
map(fn){
return new IO(fp.flowRight(fn,this._val))
}
}
let readFile = (filename)=>{
return new IO(()=>fs.readFileSync(filename,'utf-8'))
}
let print = (x)=>{
return new IO(()=>{
console.log(x);
return x
})
}
let cat = fp.flowRight(print,readFile)
let r = cat('../../package.json')._val()._val()
console.log(r);
const fs = require('fs')
const fp = require('lodash/fp')
class IO{
static of(val){
return new IO(()=>val)
}
constructor(fn){
this._val = fn
}
map(fn){
return new IO(fp.flowRight(fn,this._val))
}
join(){
return this._val()
}
flatMap(fn){
return this.map(fn).join()
}
}
let readFile = (filename)=>{
return new IO(()=>fs.readFileSync(filename,'utf-8'))
}
let print = (x)=>{
return new IO(()=>{
console.log(x);
return x
})
}
let r = readFile('../../package.json')
.map(fp.toUpper)
.flatMap(print)
.join()
console.log(r);