简体   繁体   English

Ramda.js中的无点镜片组成

[英]Point-free composition of lenses in Ramda.js

I am trying to compose functions that return lenses, to produce a new lens, and do it in a point-free style. 我正在尝试编写一些函数,这些函数可以返回镜片,以生产新镜片,并以无点样式进行操作。

This is probably a more general question about function composition. 这可能是关于功能组合的更一般的问题。 Lenses are just a case-study. 镜头只是个案例研究。 I am not interested in lenses specifically, but I want to know the general pattern for how to compose these functions in a point-free way. 我对镜头没有特别的兴趣,但是我想知道如何无点组合这些功能的一般模式。

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'

What is the pattern for composing functions so that I don't need to keep re-writing the arguments for the first function in the composition pipeline? 组成函数的模式是什么,这样我就不必继续为组成管道重新编写第一个函数的参数了?

For an egregious example: 举一个令人震惊的例子:

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);

I know you're looking at lenses only as an example, but here is one way to get something like the behavior I think you want from them. 我知道您仅以镜片为例,但这是从镜片中获得类似我认为想要的行为的一种方法。

 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> 

As to the broader composition question, lenses are generally a special case for a library like Ramda, as the composition is in the opposite order than usually expected. 对于更广泛的构图问题,镜头通常是Ramda之类库的特例,因为构图的顺序与通常预期相反。 (The technical reasons are too much to go into here.) (这里的技术原因太多了。)

But that's why this doesn't work: 但这就是为什么它不起作用的原因:

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

Ramda does allow the first function in a composition chain (rightmost in compose , leftmost in pipe to receive additional arguments. But when the composition order is reversed with lens composition, it doesn't do what you might like. Ramda确实允许构图链中的第一个函数(在compose中最右边,在pipe最左边)接收其他参数。但是当镜头构图颠倒了构图顺序时,它并不会执行您想要的操作。

So if you are having similar issues with composition in another context, please open a separate question. 因此,如果您在其他情况下也遇到构图方面的类似问题,请另开一个问题。 I'd be curious to see what you're looking for. 我很想知道你在找什么。

Rest parameters will shorten the code to: 其余参数会将代码缩短为:

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

but I'm not sure if that is really elegant. 但我不确定那是否真的很优雅。

If your intention is to write a function that accepts a path and an object, then path already exists: 如果您打算编写一个接受路径和对象的函数,则path已经存在:

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

If you're interested in removing some parameters in some of your functions, deepLens could be rewritten as follow: 如果您想删除某些函数中的某些参数,可以按以下方式重写deepLens

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

This point-free version has the added benefit that it is not limited to just three parameters. 该无点版本的另一个好处是它不仅限于三个参数。 It will work with any number of parameters: 它将与任何数量的参数一起使用:

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']);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM