如果参数的顺序错误,则指向自由

时间:2015-04-09 19:56:40

标签: javascript pointfree ramda.js

我想使用Ramda的标准函数集编写一个函数,它给出一个字典和一个键,它将增加键的值。实施例

fn('foo', {}) // => {foo: 1}
fn('foo', {foo: 1}) // => {foo: 2}

我已经非常接近,但却错过了正确的咖喱方式。

我有一个方法,它接受一个键和一个对象并返回一个:

// count :: Any -> Number
var count = R.compose(R.inc, R.defaultTo(0))

// countProp :: String -> Object -> Number
var countProp = R.curry(R.compose(count, (R.prop(R.__))))

countProp('foo', {foo:1}) // 2
countProp('foo', {}) // 1

现在我想返回一个新的数据结构

// accum :: String -> Object -> Object
var accum = R.curry(function(key, obj){
  return R.assoc(key, countProp(key, obj), obj)
})

accum('foo', {foo: 1}) // => {foo: 2}

但问题是,为了使这一点自由,我必须弄清楚如何在函数设置中获取值以正确的顺序进行咖喱。我究竟做错了什么?我应该以不同的方式设置此功能吗?我试图设置它,所以两个相关的函数都会先获取键,然后是对象,但我错过了一些东西。我应该为此考虑一个特定的Functor吗?

谢谢!

2 个答案:

答案 0 :(得分:5)

有几点:

首先,如果@ davidchambers'解决方案做你需要的,这很棒。当下一个版本的Ramda发布并添加lensProp时,它会更好,这将只是

var fooLens = R.lensProp('foo');
fooLens.map(R.inc, {foo: 1, bar: 2});  // => {foo: 2, bar: 2}

第二,原始功能和镜头版本之间存在差异:

accum('foo', {bar: 1}); //=> {"bar":1,"foo":1}
fooLens.map(R.inc, {bar: 1}); //=> {"bar":1,"foo":null}

第三,无论如何,如果你有兴趣确定如何以无点的方式包装你的功能,Ramda有几个功能可以提供帮助。有一个辅助函数nthArg除了返回一个返回函数的函数之外什么都没做,只返回它调用的外部函数的第n个参数。然后有几个函数充当compose的扩展版本,包括useWithconverge

您可以像这样使用它们:

var accum = R.converge(R.assoc, R.nthArg(0), countProp, R.nthArg(1));
accum('foo', {foo: 1, bar: 2});  // => {foo: 2, bar: 2}
accum('foo', {bar: 2});  // => {foo: 1, bar: 2}

在此代码中,converge将参数(keyobj)传递给作为参数传递的每个函数,除了第一个函数,然后传递每个函数的结果那些第一个功能。


最后,虽然这显示了一种免费编写此代码的方法,但它最终并不太可怕,但它可能不如您的早期版本清晰这不是免费的。我喜欢无点代码。但有时候我们会迷恋它,没有充分的理由让代码点免费。如果你最终不能使用lens版本,你可能需要仔细考虑无点解决方案是否比替代解决方案更清晰。

答案 1 :(得分:4)

您可以使用R.lens

const fooLens = R.lens(R.prop('foo'), R.assoc('foo'));

fooLens.map(R.inc, {foo: 1, bar: 2});  // => {foo: 2, bar: 2}
fooLens.map(R.inc, {foo: 2, bar: 2});  // => {foo: 3, bar: 2}
fooLens.map(R.inc, {foo: 3, bar: 2});  // => {foo: 4, bar: 2}

透镜可以创建一系列值,而不会通过改变它来破坏成功值的完整性。