Ramdaを使用した関数型JavaScriptプログラミング

rangle.ioで私たちは長い間関数型プログラミングに情熱を傾けており、すでにUnderscoreLodashを試しました 。 しかし最近、一見するとアンダースコアのように見えますが、小さいながらも重要な領域が異なるRamdaライブラリに出会いました。 Ramdaは、アンダースコアとほぼ同じメソッドのセットを提供していますが、機能的な構成がより簡単になるように、それらとの作業を整理します。



ラムダとアンダースコアの違いは、カレーと作曲の2つの重要な場所にあります。



カレー



カリー化とは、いくつかのパラメーターを期待する関数を1つに変換することです。パラメーターに渡すパラメーターが少なくなると、残りのパラメーターを待機する新しい関数が返されます。



R.multiply(2, 10); //  20
      
      







両方のパラメーターを関数に渡しました。



 var multiplyByTwo = R.multiply(2); multiplyByTwo(10); //  20
      
      







かっこいい。 基本的には2である新しいmultiplyByTwo関数を作成し、multiply()に組み込みました。 これで、multipliByTwoに任意の値を渡すことができます。 そしてそれは、ラムダではすべての機能がカレーをサポートしているからかもしれません。



プロセスは右から左に進みます。いくつかの引数をスキップすると、Ramdaは右側の引数をスキップしたと見なします。 したがって、配列と関数を受け取る関数は通常、関数を最初の引数として、配列を2番目の引数として期待します。 しかし、アンダースコアでは反対のことが当てはまります。



 _.map([1,2,3], _.add(1)) // 2,3,4
      
      







に対して:



 R.map(R.add(1), [1,2,3]); // 2,3,4
      
      







「最初の操作、次にデータ」アプローチと右から左へのカリー化を組み合わせることで、必要なことを設定し、それを行う関数に戻ることができます。 その後、必要なデータをこの関数に渡すことができます。 カレーは簡単で実用的です。



 var addOneToAll = R.map(R.add(1)); addOneToAll([1,2,3]); //  2,3,4
      
      







より複雑な例を次に示します。 サーバーにリクエストを行い、配列を取得して各要素からコスト値を抽出するとします。 アンダースコアを使用して、これを行うことができます:



 return getItems() .then(function(items){ return _.pluck(items, 'cost'); });
      
      







Ramdaを使用すると、不要な操作を削除できます。



 return getItems() .then(R.pluck('cost'));
      
      







R.pluck( 'cost')を呼び出すと、配列の各要素からコストを抽出する関数を返します。 そして、それがまさに.then()に渡す必要があるものです。 しかし、完全な幸福のためには、カレーと作曲を組み合わせる必要があります。



構成



関数合成とは、関数fとgを取り、h(x)= f(g(x))のような関数hを返す操作です。 Ramdaには、このためのcompose()関数があります。 これら2つの概念を組み合わせることで、より小さなコンポーネントから機能の複雑な作業を構築できます。



 var getCostWithTax = R.compose( R.multiply(1 + TAX_RATE), //   R.prop('cost') //   'cost' );
      
      







オブジェクトから値を取得し、結果に1.13を掛ける関数が判明します



標準関数「compose」は、右から左に操作を実行します。 これが直感に反すると思われる場合は、R.pipe()を使用できます。R.comipe()は、左から右にのみ機能します。



 var getCostWithTax = R.pipe( R.prop('cost'), //   'cost' R.multiply(1 + TAX_RATE) //   );
      
      







関数R.composeおよびR.pipeは、最大10個の引数を取ることができます。



もちろん、アンダースコアはカリー化と合成もサポートしますが、アンダースコアでのカリー化は使用するには不便なので、ほとんど使用されません。 Ramdaを使用すると、これら2つの手法を簡単に組み合わせることができます。



まず、ラムダに恋をしました。 彼女のスタイルは、テストしやすい拡張可能な宣言型コードを生成します。 合成は自然に実行され、理解しやすいコードにつながります。 しかし、その後...



promiseを返す非同期関数を使用すると、事態がさら​​に混乱することがわかりました。



 var getCostWithTaxAsync = function() { var getCostWithTax = R.pipe( R.prop('cost'), //   'cost' R.multiply(1 + TAX_RATE) //    1.13 ); return getItem() .then(getCostWithTax); }
      
      







もちろん、これはRamdaをまったく使用しないよりはましですが、次のようなものを取得したいと思います。



 var getCostWithTaxAsync = R.pipe( getItem, //   R.prop('cost'), //   'cost' R.multiply(1 + TAX_RATE) //   1.13 );
      
      







ただし、getItem()はpromiseを返し、R.prop()が返す関数は値を予期するため、これは機能しません。



約束の構成



Ramda開発者に連絡して、約束を自動的にアンラップするコンポジションのバージョンを提案しました。非同期関数は価値を期待する関数に関連付けることができます。 多くの議論の後、このアプローチを新しい関数の形式で実装することに同意しました:R.pCompose()およびR.pPipe()-「p」は「約束」を意味します。



R.pPipeを使用すると、必要なことを実行できます。



 var getCostWithTaxAsync = R.pPipe( getItem, //   R.prop('cost'), //   'cost' R.multiply(1 + TAX_RATE) //   1.13 ); //    cost  
      
      






All Articles