函数组合概念
函数组合:就是一个函数要经过多个函数处理才能得到最终值,这个时候可以把中间过程的函数,合并成一个函数
- 函数就像多个数据管道,把管道组合起来,让数据穿过管道得到最终结果
- 函数组合是从右到左执行
//例如下面代码
const { compose, curry } = require('folktale/core/lambda');
const { toUpper, first } = require('lodash')
let g = compose(toUpper,first)
console.log(g(['one','two'])); //ONE
loadsh中的组合函数使用
- loadsh中组合函数flow()或者flowRight(),他们都可以组合多个函数
- flow()是从左到右,flowRight是从右到左(使用多一些)
lodash的FP模块(fp模块提供的方法都是柯里化的)
lodash是数据优先,函数之后,fp模块是函数优先,数据之后
Pointfree
我们可以吧数据处理的过程定义成与数据无关的合成运算,不需要用到代表那个参数,只要简单的运算步骤合成到一起 在使用这种模式之前我们需要定义一些辅助的基本运算
- 不需要指明处理的数据
- 只需要合成运算过程,
- 需要定义一些辅助的基本运算函数
//将word wild weeb 改成 W. W. W.
let fu = fp.flowRight(fp.join('. '),fp.map(fp.flowRight(fp.toUpper,fp.first)),fp.split(' '))
console.log(fu('word wild weeb'))
Functor(函子)
需要把函数式编程中的副作用,在可控的范围内,处理异常操作,异步操作。此时我们需要用函子。 函子可以看成特殊的容器。通过一个普通的对象来实现,自定义一些方法来处理函数式编程的副作用
//普通的函子
class list{
static of(value){
return new list(value)
}
//维护一个不对外公布的值
constructor(value){
this._value = value
}
map(fun){
//返回新的处理函子
return list.of(fun(this._value))
}
}
let r = list.of()
.map(x=>x+1)
.map(x=>x*x)
//函子的值是不会拿出来的,永远保存在内部,不对外公布的。
console.log(r); //list {_value : 36}
函数式编程的运算不直接操作值,是由函子完成的 函子就是一个实现map的对象 可以把函子想象成一个封装的盒子 想要处理盒子中的值,我们需要给盒子的map方法传递一个处理的纯函数,有这个函数来处理 最终map返回一个新的盒子(函子)
MayBe(函子)
MayBe函子处理传入的 null或者undefine
//普通的函子
class MayBe{
static of(value){
return new MayBe(value)
}
//维护一个不对外公布的值
constructor(value){
this._value = value
}
map(fun){
//返回新的处理函子
return this.isNithing() ? MayBe.of(null) : MayBe.of(fun(this._value))
}
isNithing(){
return this._value === null || this._value === undefined
}
}
let r = MayBe.of()
.map(x=>null)
.map(x=>x*x)
//函子的值是不会拿出来的,永远保存在内部,不对外公布的。
console.log(r); //MayBe {_value : null}
Either(函子)
Eitherh函子是类似于if….else…来处理 异常会让函数变得不纯,eiither函子可以用来异常处理
class left{
static of(value){
return new left(value)
}
constructor(value){
this._value =value
}
map(fun){
//返回当前对象
return this
}
}
class right{
static of(value){
return new right(value)
}
constructor(value){
this._value =value
}
map(fun){
return right.of(fun(this._value))
}
}
function parseJSON(str){
try{
return right.of(JSON.parse(str))
}catch(e){
return left.of({error: e.message})
}
}
// let r = parseJSON('{name:zs}')
// console.log(r); //left {_value: { error: 'Unexpected token n in JSON at position 1' } }
let r = parseJSON('{"name":"zs"}')
.map(x=> x.name.toUpperCase())
console.log(r); //right { _value: 'ZS' }
IO(函子) —>input.output
io函子中的_value 是一个函数,这里吧函数当作值来处理 io函子可以吧不纯的动作储存到_value中,延迟执行这个不纯的操作(惰性执行),包装当前的操作纯 把不纯的操作交给调用者来处理
class IO{
static of(value){
return new IO(function(){
return value
})
}
constructor(fun){
this._value = fun
}
map(fun){
//此处使用 new IO的构造函数,是因为要吧当前函子的(value)函数,和传入的函数,组成新的函数
return new IO(fp.flowRight(fun,this._value))
}
}
let r = IO.of(process).map(x=>x.execPath)
console.log(r); //==> IO { _value: [Function] }
console.log(r._value()); // ==>/usr/local/bin/node
FolkTale库配合task函子来使用(FolkTale==>处理一些异步操作)
const { task } = require('folktale/concurrency/task')
const fs = require('fs')
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); // "version": "1.0.0",
}
})
Monad函子问题(解决函子嵌套的问题)
是具有静态的io 方法,且具有 join的一个函子,并且有flatMap方法
const fp = require('lodash/fp')
const fs = require('fs')
class IO{
static of(value){
return new IO(function(){
return value
})
}
constructor(fun){
this._value = fun
}
map(fun){
//此处使用 new IO的构造函数,是因为要吧当前函子的(value)函数,和传入的函数,组成新的函数
return new IO(fp.flowRight(fun,this._value))
}
join(){
return this._value()
}
flatMap(fun){
return this.map(fun).join()
}
}
//linux中 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 r = readFile('package.json')
.map(fp.toUpper)
.flatMap(print)
.join()
Functor,MayBe,Either,都是保存了一个值 IO函子可以延迟执行一个函数,可以控制父作用 Task就是帮组我们处理异步的任务, folktabel就是一个库。 Monad就是帮我们提供解决嵌套问题
参考
- 《你不知道的JavaScript》(上)
- 《函数式编程指南》
- First-class_Function