簡體   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