上一篇是用链式调用的方式使用Either,这一次我们尝试用函数组合,我们先使用ramda库里的composeK方法,它用于组合返回容器对象的函数(自动调用容器的chain方法来执行函数)
let R= require('ramda');
//先实现一个最轻量的Either
const Right = x =>({
chain: f => f(x),
map: f => Right(f(x)),
getOrElse:()=>x,
toString: () => `Right(${x})`,
value:x,
isRight:true
})
const Left = x => ({
chain: f => Left(x),
map: f => Left(x),
getOrElse:(v)=>v,
toString: () => `Left(${x})`,
value:x,
isRight:false
});
//1.合并判空
const fromNullable = v => v == null ? Left(v) : Right(v);
//再写一个高阶函数,用fromNullable使普通函数返回Either
const highFn = Fn => R.pipe(Fn,fromNullable);
//2.实现链式调用需要的一系列方法,根据业务来,我先随便写...
const getList = R.path(['obj','value']);
const formatProp = R.map(R.evolve({name:R.pipe(R.trim,R.toUpper)}));
const sortList = R.sortBy(R.prop('name'));
const getRes = R.composeK(
highFn(sortList),
highFn(formatProp),
highFn(getList),
fromNullable,
)
const result = getRes(obj);
console.log(result.value);
但是,这样调用并不好看,因为要用higeFn来饲养普通方法,已使得它们返回Either对象,这样的模版代码应该能去掉,这就必须自己来实现compose组合函数了,比如这样:
const compose = (...Fns)=> R.composeWith((Fn, res) => res.chain(highFn(Fn)) )(Fns);
我们使用了ramda的composeWith方法来自定义组合逻辑,在方法链的调用过程中自动饲养函数并自动调用容器的chain方法。
完整代码如下:
let R= require('ramda');
//先实现一个最轻量的Either
const Right = x =>({
chain: f => f(x),
map: f => Right(f(x)),
getOrElse:()=>x,
toString: () => `Right(${x})`,
value:x,
isRight:true
})
const Left = x => ({
chain: f => Left(x),
map: f => Left(x),
getOrElse:(v)=>v,
toString: () => `Left(${x})`,
value:x,
isRight:false
});
//1.合并判空
const fromNullable = v => v == null ? Left(v) : Right(v);
//再写一个高阶函数,用fromNullable使普通函数返回Either
const highFn = Fn => R.pipe(Fn,fromNullable);
//2.实现链式调用需要的一系列方法,根据业务来,我先随便写...
const getList = R.path(['obj','value']);
const formatProp = R.map(R.evolve({name:R.pipe(R.trim,R.toUpper)}));
const sortList = R.sortBy(R.prop('name'));
const compose = (...Fns)=> R.composeWith((Fn, res) => res.chain(highFn(Fn)) )(Fns);
const getRes = compose(
sortList,
formatProp,
getList,
fromNullable,
)
const obj = {
obj:{
value:[
{name:'xx'},
{name:'yy'}
]
}
}
const result = getRes(obj);
console.log(result.value);
下面,来实现验证,还是用自定义的compose
let R= require('ramda');
//先实现一个最轻量的Either
const Right = x =>({
chain: f => f(x),
map: f => Right(f(x)),
getOrElse:()=>x,
toString: () => `Right(${x})`,
value:x,
isRight:true
})
const Left = x => ({
chain: f => Left(x),
map: f => Left(x),
getOrElse:(v)=>v,
toString: () => `Left(${x})`,
value:x,
isRight:false
});
//1.实现一个逻辑处理函数,逻辑是:遇到错误(Error对象)就终止执行(放入Left容器)。
const fromError = v => v instanceof Error ? Left(v) : Right(v);
const highFn = Fn => R.pipe(Fn,fromError);
//2.业务方法
const mobileReg = /^1[123456789]\d{9}$/;
const vEmpty = v => v.length !== 0 ? v : new Error('手机号码为必须项!');
const vMobile = v => mobileReg.test(v.trim()) ? v : new Error('请输入正确的手机号码!')
const compose = (...Fns)=> R.composeWith((Fn, res) => res.chain(highFn(Fn)) )(Fns);
//3.组合方法
const getResult = compose(
vMobile,
vEmpty,
Right
)
const result = getResult('');
console.log(result.isRight);
还有一种执行逻辑,就是收集错误,这个要求方法链遇到错误也必须执行下去,Either目前满足不了,这很可能需要实现一个新的容器。我们先实现一个新容器,再尝试不用新容器而仍然用Either来执行这种逻辑,因为我们希望把Either当成一个万能的分支处理容器。
(以上代码都经过测试,直接可用。)