一、pointfree
把数据处理的过程定义成与数据无关的合成运算,不需要用到代表数据的参数,只需将运算步骤合成,使用之前需定义一些辅助运算函数
- 不需要指明处理的数据
- 需要合成运算过程
- 需要定义一些辅助的基本运算函数
这就是函数组合,只是更加抽象
const f1 = fp.flowRight(fp.join('-'), fp.map(fp.toLower), fp.split(' '))
const f2 = fp.flowRight(fp.replace(/\s+/g, '_'), fp.toLower)
f2('HELLO WORLD')
-----------------------
//world wild web => W. W. W
const change = fp.flowRight(fp.join('. '), fp.map(
fp.flowRight(fp.first, fp.toUpper)
), fp.split(' '))
change('world wild web')
二、Functor 函子
如何在函数式编程中把副作用控制在可控范围内、异常处理、异步操作等
定义
容器:包含值和值的变形关系(这个关系就是函数)
函子:一个特殊的容器,通过一个普通对象来实现,具有map方法,可以运算一个函数对值进行处理(变形关系)
class Container{
static of(value){
return new Container(value)
}
constructor(value){
this._value = value
}
map(fn){
//return new Container(fn(this._value))
return Container.of(fn(this._value))
}
}
//let r = new Container(5).map(x => x + 1).map(x => x * x)
let r = Container.of(5).map(x => x + 1).map(x => x * x)
总结
函数式编程的运算不直接操作值,而是由函子完成
函子就是一个实现了map契约的对象
可以把函子当成一个盒子,盒子里封装了一个值
处理盒子中的值,需给盒子的map方法传递一个处理值的函数
map方法最终返回一个包含新值的盒子(函子)
三、maybe函子
- 编程过程中可能会遇到很多错误,需对此进行处理
- maybe函子的作用就是可以对外部的空值情况处理
- 多次调用时,出现了null,无法确定是在哪一步出现,调试不便
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
}
}
四、Either函子
两者中的任何一个,用来做异常处理 ```javascript class Left{ static of(value){
} constructor(value){return new Left(value)
} map(fn){this._value = value
} } class Right{ static of(value){return this
} constructor(value){return new Right(value)
} map(fn){this._value = value
} }return Right.of(fn(this._value))
function parseJSON(str){ try{ return Right.of(JSON.parse(str)) }catch(e){ return Left.of({error: e.message}) } } let r1 = parseJSON(‘{name : zs}’) let r2 = parseJSON(‘{“name” : “zs”}’) let r3 = parseJSON(‘{“name” : “zs”}’).map(x => x.name.toUpperCase())
<a name="4coAA"></a>
# 五、Task异步执行
- forktale一个标准的函数式编程库
- 只提供了一些函数式处理操作,如:compose、curry等。一些函子Task,Either、MayBe等
```javascript
const {compose, curry} = require('forktale/core/lambda')
const {toUpper, first} = require('lodash/fp')
let f1 = curry(2, (x, y) => x+y)
console.log(f1(1,2))
console.log(f1(1)(2))
let f2 = compose(toUpper, first)
console.log(f2(['one', 'two']))
Task函子
const {task} = require('forktale/concurrency/task')
const fs = require('fs')
const {split, find} = require('lodash/fp')
function readFile(filePath){
return task(resolver => {
fs.readFile(filePath,'utf-8',(error, data) => {
if (error) resolver.reject(error)
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函子
实现了of静态方法的函子
of方法是为了避免使用new来创建对象,更深层的含义是of方法用来把值放到上下文
把值放到容器中,使用map来处理值
七、IO函子
IO函子的_value是一个函数,这里是把函数作为值来处理
IO函子可以把不纯的动作存到_value中,延迟执行这个不纯的操作(惰性执行)
把不纯的操作交给调用者来执行
class IO{
static of(x){
return new IO(function() {
return x
})
}
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._value)
let readFile = function(filePath){
return new IO(function(){
return fs.readFileSync(filePath,'utf-8')
})
}
let print = function(x){
return new IO(function(){
console.log(x)
return x
})
}
let cat = fp.flowRight(print, readFile)
//IO(IO(x)) 函子嵌套时调用value不方便
let r = cat('package.json')._value()._value()
八、Monad函子
Monad函子是可以变扁的Pointed函子
一个函子如果具有join和of两个方法并遵守一些定律就是一个Monad
class IO{
static of(x){
return new IO(function() {
return x
})
}
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()
}
}