Ramda.js中的无点镜片组成

时间:2019-01-15 16:24:07

标签: javascript functional-programming ramda.js

我正在尝试编写返回镜头的功能,以生产新镜头并以无点样式进行操作。

这可能是关于函数组成的更一般的问题。镜头只是个案例研究。我对镜头没有特别的兴趣,但是我想知道如何无点地组合这些功能的一般模式。

const obj = {a: {x: 0}, b: {x: 42}};

// this won't work, but I want it to work
const pointFreeComposedLens = R.compose(R.lensProp, R.lensProp('x'));
R.view(pointFreeComposedLens('a'), obj); // returns 'undefined'

// this works
const pointyComposedLens = key => R.compose(R.lensProp(key), R.lensProp('x'));
R.view(pointyComposedLens('a'), obj); // returns '0'

组成函数的模式是什么,这样我就不必继续重写组成管线中第一个函数的参数了?

举个令人震惊的例子:

const deepLens = (a, b, c) => R.lensPath([a, b, c]);

// This works, but is tedious & verbose
const extraDeep = (a, b, c, x) => R.compose(deepLens(a,b,c), R.lensProp(x));
const gammaDeep = (a, b, c, y) => R.compose(deepLens(a,b,c), R.lensProp(y));

// Doesn't work, but it would be nicer to write:
const extraDeep = x => R.compose(deepLens, R.lensProp(x));

// and call it like so:
R.view(extraDeep('a','b','c','x'), obj);

3 个答案:

答案 0 :(得分:2)

我知道您仅以镜片为例,但这是从镜片中获得我认为想要的行为的一种方法。

const {lensPath, compose, lens, view} = R

const deepLens = (a, b, c) => lensPath([a, b, c]);
const deeper = (lens, ...args) => compose(lens, lensPath(args))

const cLens = deepLens('a', 'b', 'c')
const obj =  {a: {b: { c: {d: 1, e: 2, f: {g: 3, h: 4, i: {j: 5, k: 6}}}}}}

console.log(view(cLens, obj)) //=> {d: 1, e: 2, f: {g: 3, h: 4, i: {j: 5, k: 6}}}
console.log(view(deeper(cLens, 'f', 'g'), obj)) //=> 3

const fLens = deeper(cLens, 'f')

console.log(view (fLens, obj)) //=> {g: 3, h: 4, i: {j: 5, k: 6}}

const jLens = deeper(cLens, 'f', 'i', 'j')
// or jLens = deeper(fLens, 'i', 'j')

console.log(view(jLens, obj)) //=> 5
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>

对于更广泛的构图问题,镜头通常是Ramda之类的图书馆的特例,因为构图的顺序与通常预期相反。 (这里的技术原因太多了。)

但这就是为什么这不起作用:

  
const extraDeep = x => R.compose(deepLens, R.lensProp(x));

Ramda确实允许构图链中的第一个功能(compose的最右边,pipe的最左边)接收其他参数。但是当镜头构图的构图顺序颠倒时,它不起作用你可能会喜欢什么。

因此,如果您在其他情况下也遇到构图方面的类似问题,请打开一个单独的问题。我很想知道您在寻找什么。

答案 1 :(得分:1)

Rest参数会将代码缩短为:

 const extraDeep = (...rest) => last => R.compose(deepLens(...rest), R.lensProp(last))(rest.pop());

但是我不确定那是否真的很优雅。

答案 2 :(得分:1)

如果您打算编写一个接受路径和对象的函数, 那么path已经存在:

R.path(['a', 'b'], {a: {b: 10}}); //=> 10

如果您想删除某些函数中的某些参数,可以deepLens进行如下重写:

const deepLens = R.unapply(R.lensPath);

此无点版本具有附加优势,即它不仅限于三个参数。它将与任意数量的参数一起使用:

deepLens('a', 'b');           //=> R.lensPath(['a', 'b']);
deepLens('a', 'b', 'c');      //=> R.lensPath(['a', 'b', 'c']);
deepLens('a', 'b', 'c', 'd'); //=> R.lensPath(['a', 'b', 'c', 'd']);