繁体   English   中英

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

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

我的团队正在从 Lodash 迁移到 Ramda,并进入函数式编程风格的更深层次。 我们一直在尝试更多的compose等,并且遇到了这种模式:

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

(我们当然可以省略=> 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 compose行。 在我给出的示例中,将是id然后obj然后state用于getStuff

如果不是getOtherStuff function,我们可以R.curry myFunc

有没有一个优雅的解决方案,这将是免费的? 这似乎是 FP 中很常见的模式。

这是不将无点推得太远的一个理由。 我设法制作了上述内容的无点版本。 但我不能真正理解它,我真的怀疑我的代码的大多数读者也会。 这里是,

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

请注意o只是 Ramda 通常的可变参数compose function 的(Ramda-curried)二进制版本。

我并没有真正弄清楚这一点。 我作弊了。 如果您可以阅读 Haskell 代码并用它编写一些基本的东西,您可以使用精彩的Pointfree.io站点将指向代码转换为无点代码。

我输入了您的 function 的 Haskell 版本:

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

并得到了这个:

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

其中,有点磕磕绊绊,我能够转换到上面的版本。 我知道我必须使用o而不是compose ,但花了一点时间才明白我必须使用liftN (2, o)而不仅仅是lift (o) 我还没有试图弄清楚为什么,但 Haskell 真的不明白 Ramda 的魔法柯里化,我猜它与此有关。

这个片段展示了它的实际效果,你的函数被删除了。

 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>

不值得

虽然这很有趣,但我永远不会在生产代码中使用它。 现在读了一遍,我开始明白了。 但是一个月后我就忘记了,很多读者可能永远都不会明白。

无点可以导致一些优雅的代码。 但只有在这样做时才值得使用; 当它掩盖你的意图时,跳过它。

我不知道为什么你不能咖喱:

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

或者

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

我不确定我是否在这里看到了无点解决方案(就目前而言)。 有一些不太直观的组合器可能适用。 我要考虑的另一件事是 getStuff 和 getOtherStuff 函数的签名是否按正确的顺序排列。 如果按以下顺序定义它们可能不会更好:obj,state,id。

问题是 obj 在两个不同的功能中需要。 也许重述 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)

我发现将多个参数函数视为采用单个参数的函数很有帮助,该参数恰好是某种元组。

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

=====

根据关于组合器的问题进行更新。 http://www.angelfire.com/tx4/cus/combinator/birds.html有八哥(S)组合:

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

以更es6的方式编写

const S = a => b => c => a(c, 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.

在你的例子中,我们可以这样写

S(getOtherStuff, getStuff, obj)

但现在我看这可能行不通。 因为 getStuff 在被应用到 getOtherStuff 之前并不完全满足......您可以开始拼凑一个谜题的解决方案,这有时很有趣,但也不是您想要的产品代码。 有一本书https://en.wikipedia.org/wiki/To_Mock_a_Mockingbird人们喜欢它,尽管它对我来说具有挑战性。

我最大的建议是开始将所有功能视为一元。

暂无
暂无

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

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