【发布时间】:2017-06-13 08:00:26
【问题描述】:
我有一些丑陋的数据,这需要很多丑陋的空检查。我的目标是编写一套函数,以无点、声明式的风格访问/修改它,使用 Maybe monad 将空检查保持在最低限度。理想情况下,我可以将 Ramda 与 monad 一起使用,但效果不是很好。
这行得通:
const Maybe = require('maybe');
const R = require('ramda');
const curry = fn => (...args) => fn.bind(null, ...args);
const map = curry((fn, monad) => (monad.isNothing()) ? monad : Maybe(fn(monad.value())));
const pipe = (...fns) => acc => fns.reduce((m, f) => map(f)(m), acc);
const getOrElse = curry((opt, monad) => monad.isNothing() ? opt : monad.value());
const Either = (val, d) => val ? val : d;
const fullName = (person, alternative, index) => R.pipe(
map(R.prop('names')),
map(R.nth(Either(index, 0))),
map(R.prop('value')),
map(R.split('/')),
map(R.join('')),
getOrElse(Either(alternative, ''))
)(Maybe(person));
但是,必须输入 'map()' 十亿次似乎不是很干,也不是很好看。我宁愿有一个特殊的管道/组合函数,它将每个函数包装在一个 map() 中。
注意我是如何使用 R.pipe() 而不是我的自定义 pipe() 的?我的自定义实现总是抛出一个错误,'isNothing() is not a function,' 在执行传递给它的最后一个函数时。
我不确定这里出了什么问题,或者是否有更好的方法可以做到这一点,但我们非常感谢任何建议!
【问题讨论】:
-
您能否举例说明
person中的数据是什么样的? -
你对作曲做了什么测试?你应该能够做到
R.compose(R.props(..), R.nth(..), R.split(...)然后R.map(composedFuncs, Maybe(person)) -
提示:在现代浏览器中
.bind(thisArg)速度非常快。同时,.bind(thisArg, ...funcArgs)非常慢。在这种情况下,闭包是更好(更快)的选择:const curry = fn => (...args) => (...brgs) => fn.apply(null, args.concat(brgs)); -
这就是我不喜欢 FP 的地方。当我不得不扭曲和弯曲时,只是为了适应范式。我喜欢声明式风格,因为正确使用它可以作为注释代码的两倍,但如果我必须将它用于每个微小的步骤/操作/操作符,则不会。我会将您的代码编写为
const fullName = (person, alternative = "", index=0) => string( fetch(person, ["names", index, "value"]) ) .split("/").join("") || alternative;,其中我的两个实用程序string和fetch包含的代码不超过您所有的实用程序。但这并不真正符合主题,或者是吗? -
@atomrc 数据看起来像{名称:[{value:'Bob / Johnson',来源:{...}},{...etc.}]}。没有一个 Ramda 函数可以自己访问 Maybe 值,所以我试图以某种方式分离 map 并使用自定义 compose/pipe 函数。
标签: javascript functional-programming ramda.js