简体   繁体   English

如何在 Ramda 中以无点风格组合咖喱函数?

[英]How to compose curried functions in a point free style in Ramda?

My team is moving from Lodash to Ramda and entering the deeper parts of Functional Programming style.我的团队正在从 Lodash 迁移到 Ramda,并进入函数式编程风格的更深层次。 We've been experimenting more with compose , etc, and have run into this pattern:我们一直在尝试更多的compose等,并且遇到了这种模式:

const myFunc = state => obj => id => R.compose(
  R.isNil,
  getOtherStuff(obj),
  getStuff(state)(obj)
)(id)

(We can of course omit the => id and (id) parts. Added for clarity.) (我们当然可以省略=> id(id)部分。为清楚起见添加。)

In other words, we have lots of functions in our app (it's React+Redux for some context) where we need to compose functions that take similar arguments or where the last function needs to get all its arguments before passing on to the next function in the compose line. In other words, we have lots of functions in our app (it's React+Redux for some context) where we need to compose functions that take similar arguments or where the last function needs to get all its arguments before passing on to the next function in compose行。 In the example I gave, that would be id then obj then state for getStuff .在我给出的示例中,将是id然后obj然后state用于getStuff

If it weren't for the getOtherStuff function, we could R.curry the myFunc .如果不是getOtherStuff function,我们可以R.curry myFunc

Is there an elegant solution to this that would be point-free?有没有一个优雅的解决方案,这将是免费的? This seems a common enough pattern in FP.这似乎是 FP 中很常见的模式。

Here's one rationale for not pushing point-free too far.这是不将无点推得太远的一个理由。 I managed to make a point-free version of the above.我设法制作了上述内容的无点版本。 But I can't really understand it, and I really doubt that most readers of my code would either.但我不能真正理解它,我真的怀疑我的代码的大多数读者也会。 Here it is,这里是,

const myFunc2 = o (o (o (isNil)), o (liftN (2, o) (getOtherStuff), getStuff)) 

Note that o is just a (Ramda-curried) binary version of Ramda's usual variadic compose function.请注意o只是 Ramda 通常的可变参数compose function 的(Ramda-curried)二进制版本。

I didn't really figure this out.我并没有真正弄清楚这一点。 I cheated.我作弊了。 If you can read Haskell code and write some basic things with it, you can use the wonderful Pointfree.io site to convert pointed code into point-free.如果您可以阅读 Haskell 代码并用它编写一些基本的东西,您可以使用精彩的Pointfree.io站点将指向代码转换为无点代码。

I entered this Haskell version of your function:我输入了您的 function 的 Haskell 版本:

\state -> \obj -> \id -> isNil (getOtherStuff obj (getStuff state obj id))

and got back this:并得到了这个:

((isNil .) .) . liftM2 (.) getOtherStuff . getStuff

which, with a little stumbling, I was able to convert to the version above.其中,有点磕磕绊绊,我能够转换到上面的版本。 I knew I'd have to use o rather than compose , but it took a little while to understand that I'd have to use liftN (2, o) rather than just lift (o) .我知道我必须使用o而不是compose ,但花了一点时间才明白我必须使用liftN (2, o)而不仅仅是lift (o) I still haven't tried to figure out why, but Haskell really wouldn't understand Ramda's magic currying, and I'm guessing it has to do with that.我还没有试图弄清楚为什么,但 Haskell 真的不明白 Ramda 的魔法柯里化,我猜它与此有关。

This snippet shows it in action, with your functions stubbed out.这个片段展示了它的实际效果,你的函数被删除了。

 const isNil = (x) => `isNil (${x})` const getStuff = (state) => (obj) => (id) => `getStuff (${state}) (${obj}) (${id})` const getOtherStuff = (obj) => (x) => `getOtherStuff (${obj}) (${x})` const myFunc = state => obj => id => R.compose( isNil, getOtherStuff (obj), getStuff (state) (obj) )(id) const myFunc2 = o (o (o (isNil)), o (liftN (2, o) (getOtherStuff), getStuff)) console.log ('Original: ', myFunc ('state') ('obj') ('id')) console.log ('Point-free: ', myFunc2 ('state') ('obj') ('id'))
 .as-console-wrapper {min-height: 100%;important: top: 0}
 <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script> <script> const {o, liftN} = R </script>

Not worth it不值得

While this is very interesting, I would never use that in production code.虽然这很有趣,但我永远不会在生产代码中使用它。 Reading it over now, I'm starting to get it.现在读了一遍,我开始明白了。 But I will have forgotten it in a month, and many readers would probably never understand.但是一个月后我就忘记了,很多读者可能永远都不会明白。

Point-free can lead to some elegant code.无点可以导致一些优雅的代码。 But it's worth using only when it does so;但只有在这样做时才值得使用; when it obscures your intent, skip it.当它掩盖你的意图时,跳过它。

I don't know why you can't curry though:我不知道为什么你不能咖喱:

const myFunc = curry(state, obj) => R.compose(
  R.isNil,
  getOtherStuff(obj),
  getStuff(state)(obj)
));

or或者

const myFunc = curry(state, obj, id) => R.compose(
  R.isNil,
  getOtherStuff(obj),
  getStuff(state)(obj)
)(id));

I am not sure I see a point free solution here (as it stands).我不确定我是否在这里看到了无点解决方案(就目前而言)。 There are some less intuitive combinators that may apply.有一些不太直观的组合器可能适用。 The other thing I would consider is whether the getStuff and getOtherStuff functions have their signatures in the correct order.我要考虑的另一件事是 getStuff 和 getOtherStuff 函数的签名是否按正确的顺序排列。 Maybe it't be better if they were defined in this order: obj, state, id.如果按以下顺序定义它们可能不会更好:obj,state,id。

The problem is that the obj is needed in two differnt funcitons.问题是 obj 在两个不同的功能中需要。 Perhaps restating getStuff to return a pair and getOtherStuff to take a pair.也许重述 getStuff 以返回一对,而 getOtherStuff 以获取一对。

const myFunc = R.compose(
  R.isNil,         // val2 -> boolean
  snd,             // (obj, val2) -> val2
  getOtherStuff,   // (obj, val) -> (obj, val2)
  getStuff         // (obj, state, id) -> (obj, val)
);

myFunc(obj)(state)(id)

I have found it helpful to think of multiple parameter functions as functions that take a single parameter which happens to be a tuple of some sort.我发现将多个参数函数视为采用单个参数的函数很有帮助,该参数恰好是某种元组。

getStuff = curry((obj, state, id) => {
   const val = null;
   return R.pair(obj, val);
}

getOtherStuff = curry((myPair) => {
   const obj = fst(myPair)
   const val2 = null;
   return R.pair(obj, val2);
}

fst = ([f, _]) => f
snd = ([_, s]) => s

===== =====

Update per the question on combinators.根据关于组合器的问题进行更新。 From http://www.angelfire.com/tx4/cus/combinator/birds.html there is the starling (S) combinator:http://www.angelfire.com/tx4/cus/combinator/birds.html有八哥(S)组合:

λa.λb.λc.(ac)(bc)

written in a more es6 way以更es6的方式编写

const S = a => b => c => a(c, b(c))

or a function that takes three parameters a,b,c.或一个 function,它采用三个参数 a、b、c。 We apply c to a leaving a new function, and c to b leaving whatever which is immediately applied to the function resuilting from c being applied to a. We apply c to a leaving a new function, and c to b leaving whatever which is immediately applied to the function resuilting from c being applied to a.

in your example we could write it like在你的例子中,我们可以这样写

S(getOtherStuff, getStuff, obj)

but that might not work now that I look at it.但现在我看这可能行不通。 because getStuff isn't fully satisfied before being being applied to getOtherStuff... You can start to peice together a solution to a puzzle, which is sometimes fun, but also not something you want in your production code.因为 getStuff 在被应用到 getOtherStuff 之前并不完全满足......您可以开始拼凑一个谜题的解决方案,这有时很有趣,但也不是您想要的产品代码。 There is the book https://en.wikipedia.org/wiki/To_Mock_a_Mockingbird people like it, though it is challenging for me.有一本书https://en.wikipedia.org/wiki/To_Mock_a_Mockingbird人们喜欢它,尽管它对我来说具有挑战性。

My biggest advice is start thiking about all functions as unary.我最大的建议是开始将所有功能视为一元。

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

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