関数型プログラミングを意識的に放棄することは可能ですか?

関数型プログラミングは、JavaScriptエコシステム、Linq for C#、さらにはJavaの高次関数など、コアプログラミングの世界の多くに浸透しています。 これは、2018年のJavaの外観です。



getUserName(users, user -> user.getUserName());
      
      





関数型プログラミングは非常に便利で便利なので、私が見る限りでは、すべての現代の共通言語に浸透しています。



しかし、すべてがバラ色ではありません。 多くの開発者は、ソフトウェアへのアプローチにおけるこの構造的変化に抵抗しています。 正直なところ、今日では、FPの概念に関する知識を必要としないJavaScriptに関連する作業を見つけるのは困難です。



関数型プログラミングは、両方の主要なフレームワークの基礎です:React(アーキテクチャと一方向のデータ転送は、一般的な可変DOMを回避するために作成されました)とAngular(RxJSは、高次関数を介してストリームを処理するフレームワーク全体で広く使用されているユーティリティオペレーターのライブラリです)。 Reduxおよびngrx / storeも機能します。



初心者はFPのアプローチに怖がる可能性があります。すぐに仕事に取りかかるために、チームの誰かがFPを放棄してコードベースに慣れることを提案する場合があります。



FI自体や現代のプログラミングエコシステムへの影響に精通していない管理者にとっては、そのような提案は合理的と思われるかもしれません。 結局のところ、PLOは30年間忠実に私たちに奉仕しませんか? すべてをそのままにしてみませんか?



言葉遣い自体に対処しましょう。 禁止は政治レベルで何を意味しますか?



関数型プログラミングとは何ですか?



私のお気に入りの定義:



関数型プログラミングは、 純粋な関数を構成の分割不可能な単位として使用し、 一般的な可変状態と副作用を回避するというパラダイムです



ネット機能:





つまり、FP本質は次のように削減されます。





すべてをまとめると、純粋な機能(分割不可能な構成単位)の形のビルディングブロックを使用したソフトウェア開発が可能になります。



ところで、 Alan Kay (現代OOPの創設者)は、オブジェクト指向プログラミングの本質は次のとおりであると考えています。





したがって、OOPは、一般的な可変状態および副作用回避するための単なる別の方法です。



明らかに、FPの反対はOOPではなく、非構造化の手続き型プログラミングです。



Smalltalk言語(Alan KayがOOPの基礎を築いた)はオブジェクト指向で機能的であり、どちらかを選択する必要性は異質で受け入れがたい考えです。



JavaScriptについても同じことが言えます。 この言語を開発するためにブレンダンアイヒが雇われたとき、彼の主なアイデアは次のものを作成することでした。





JavaScriptでは、任意の1つのパラダイムに準拠しようとすることができますが、良かれ悪しかれ、両方とも密接にリンクされています。 JavaScriptのカプセル化は、FPの概念であるクロージャーに依存しています。



おそらくあなたはすでに関数型プログラミングを使用しているかもしれません。



FPを使用しない方法



関数型プログラミングを避けるには、純粋な関数の使用を避ける必要があります。 しかし、そのようなコードはきれいになる可能性があるため、そのようなコードを書くことはできません。



 const getName = obj => obj.name; const name = getName({ uid: '123', name: 'Banksy' }); // Banksy
      
      





コードをリファクタリングして、FPを参照しないようにします。 パブリックプロパティを持つクラスを作成できます。 カプセル化は使用されないため、このOOPを呼び出すのは誇張です。 おそらく「手続き型オブジェクトプログラミング」ですか?



 class User { constructor ({name}) { this.name = name; } getName () { return this.name; } } const myUser = new User({ uid: '123', name: 'Banksy' }); const name = myUser.getName(); // Banksy
      
      





おめでとうございます! 2行のコードを11に変えただけでなく、制御されない外部突然変異の可能性も導入しました。 そして、見返りに何を得ましたか?



まあ、実際には、何もありません。 実際、汎用性とコードの大幅な節約の可能性を失いました。



前のgetName()関数は、着信オブジェクトで機能しました。 新しい関数も機能します(JSであり、メソッドを任意のオブジェクトに委任できるため)が、非常に不器用です。 2つのクラスが通常の親クラスから継承することを許可できますが、これはクラス間の不要な関係の存在を意味します。



再利用性を忘れてください。 トイレに流しました。 コードの複製は次のようになります。



 class Friend { constructor ({name}) { this.name = name; } getName () { return this.name; } }
      
      





机の後ろにいる勤勉な学生は、「さあ、クラスの人を作ろう!」と叫ぶでしょう。



次に:

 class Country { constructor ({name}) { this.name = name; } getName () { return this.name; } }
      
      





「しかし、これらは異なるタイプです。 もちろん 、国に個人クラスメソッドを適用することはできません!」



これに私は答えます:「なぜ?」



関数型プログラミングの信じられないほどの利点の1つは、プログラミングが簡単に一般化できることです。 これを「デフォルトで一般化」と呼ぶことができます一般化された要件を満たす任意のタイプで機能する単一の関数を作成する機能です



Javaプログラマーへの注意:静的型付けについては話していません。 一部のFP言語には優れた静的型システムがありますが、それでも構造化型および/またはHKT(高種類の型)を利用しています。



これは明らかな例ですが、このトリックで得られるコードの節約量は膨大です。



これにより、 autoduxなどのライブラリは、ゲッター/セッターペア(およびその他多く)から作成されたオブジェクトのドメインロジック自動的に生成できます。 また、ドメインロジックコードの量を半分、またはそれ以上削減することもできます。



これ以上の高次機能はありません



多くの(すべてではない)高階関数は、同じ入力データを受け取ったときに純粋な関数を利用して同じ値を返すため、副作用が発生せずに.map(), .filter(), .reduce()



などの関数を使用することはできません.map(), .filter(), .reduce()







 const arr = [1,2,3]; const double = n => n * 2; const doubledArr = arr.map(double);
      
      





次のようになります。



 const arr = [1,2,3]; const double = (n, i) => { console.log('Random side-effect for no reason.'); console.log('Oh, I know, we could directly save the output to the database and tightly couple our domain logic to our I/O. That will be fine. Nobody else will need to multiply by 2, right?'); saveToDB(i, n); return n * 2; }; const doubledArr = arr.map(double);
      
      





安らぎ、作曲機能。 1958–2018



高次コンポーネントポイントフリー構成を忘れて、ページのエンドツーエンド機能をカプセル化します。 この便利な宣言構文はタブーに変わります。



 const wrapEveryPage = compose( withRedux, withEnv, withLoader, withTheme, withLayout, withFeatures({ initialFeatures }) );
      
      





これらすべてを手動で各コンポーネントにインポートする必要があります。さらに悪いことに、クラスの継承を伴う混乱した柔軟性のない階層に突入する必要があります(最も意識の高い市民、さらに(特に) オブジェクト指向プログラミング規範は アンチパターンとして正当に認識されています )。



さらば、約束と非同期/待ち



約束はモナドです。 技術的には、それらはカテゴリー理論に関連していますが、約束はまだFPに関連していると聞きました。



正直なところ、モナドとファンクターを取り除くことはそれほど悪くないでしょう。 それらは公開するよりもはるかに簡単です! ファンクタとモナドの一般的な概念について話す前にArray.prototype.map



とpromisesの使用方法を人々に教えるだけではありません。



それらを適用する方法を知っていますか? したがって、ファンクターとモナドを理解するのは中途半端です。



したがって、関数型プログラミングを避けるために





そして、さよならを言う:





関数型プログラミングを避けますか? 問題ありません。

またはちょっと...



All Articles