関数型プログラミングは、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についても同じことが言えます。 この言語を開発するために
ブレンダンアイヒが雇われたとき、彼の主なアイデアは次のものを作成することでした。
- ブラウザ用スキーム(FP)
- Java(OOP)のように見える言語
JavaScriptでは、任意の1つのパラダイムに準拠しようとすることができますが、良かれ悪しかれ、両方とも密接にリンクされています。 JavaScriptのカプセル化は、FPの概念であるクロージャーに依存しています。
おそらくあなたはすでに関数型プログラミングを使用しているかもしれません。
FPを使用しない方法
関数型プログラミングを避けるには、純粋な関数の使用を避ける必要があります。 しかし、そのようなコードはきれいになる可能性があるため、そのようなコードを書くことはできません。
const getName = obj => obj.name; const name = getName({ uid: '123', name: '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();
おめでとうございます! 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の使用方法を人々に教えるだけではありません。
それらを適用する方法を知っていますか? したがって、ファンクターとモナドを理解するのは中途半端です。
したがって、関数型プログラミングを避けるために
- 人気のあるJavaScriptフレームワークとライブラリを使用しないでください(FPのためにすべてが裏切られます!)
- 純粋な関数を書かない
- 多くの組み込みJavaScript機能を使用しないでください。ほとんどの数学関数(純粋であるため)、不変の文字列および配列メソッド、
.map(), .filter(), .forEach()
、async / await - 不要なクラスを書く
- 文字通りすべてのゲッターとセッターを手動で入力して、ドメインロジックを2倍(またはさらに増やす)にします
- 「読み取り可能で明示的な」命令型アプローチを採用し、ドメインロジックとレンダリングおよびネットワークI / Oを混同します。
そして、さよならを言う:
- タイムトラベルデバッグ
- 元に戻す/やり直す機能の簡単な開発
- 堅牢で一貫性のある単体テスト
- D / Iなしのスタブとテスト
- ネットワークI / Oに依存しない高速ユニットテスト
- テスト、デバッグ、保守に便利な小さなコードベース
関数型プログラミングを避けますか? 問題ありません。
またはちょっと...