この投稿は、「FORUM PHP 2024」での Gina Banyard による技術講演に大きく影響を受けています。
いくつかの同義語から始めましょう:
「PHP モナド」で Google 検索すると、関数型プログラミング、バインディング、スタック、さらには難解な数学 (ファンクター、モノイドなど) など、他の概念がすぐに表示されます。
怖がらないでください。
本質的に、モナドはさまざまな方法で実装できるパターンです。
実行する操作がある場合は、通常どおりカスタム オブジェクトとヘルパーを定義するだけで済みます。
それでは、なぜわざわざ別の概念を使うのでしょうか?
私見ですが、効率を維持する必要があるので、これはまだ良い質問ですが、古典的なアプローチには一般的な制限があります。
モナドはオプションの (またはまだ利用できない) 値をより一貫して処理できるようになります。
最新のプロジェクトには静的分析用のツールが含まれていますが、PHP 例外は型指定されません。
言い換えると、ツールは関数シグネチャ内の例外を検出できないため、コードが例外を正しく処理しているかどうかを判断できません。
これをテストするために、開発チームは通常、機能テストを作成しますが、静的分析による早期検出の方が信頼性が高くなります。
出典: "Les Exception : le trou dans la raquette du typage" (fr)
モナドを使用すると、カスタム列挙型ケース (FileErrors::AccessDenied など) など、あらゆる場合に型付きオブジェクトを取得できるため、エラーはシステムに型付けされます。
堅牢なログ システムを構築するのは困難な場合があります。文字列や呼び出しは簡単に複製できます。
すべてをハードコーディングする代わりに、log() というカスタム ヘルパーを定義し、プロジェクト内のあらゆる場所でそれを使用することになるでしょう。
これはコードを DRY に保つことを目的としていますが、特定のケースではより複雑な関数を作成できない可能性があります。
機能的なアプローチは、そのようなグローバル ヘルパーの使用では構成されません。代わりに、他の関数をラップするモナドを実装します:
final class LoggerMonad { public function __construct( public mixed $data, public array $logs = [], ) {} public function bind(callable $fn) { $resultLoggerMonad = $fn($this->data); return new LoggerMonad( $resultLoggerMonad->data, [...$this->logs, ...$resultLoggerMonad->logs], ); } } function loggify(callable $fn): Closure { return function ($value) use ($fn) { $name = (new ReflectionFunction($fn))->name; $log = [ 'Running '. $name .'('. var_export($value, true) .')' ]; return new LoggerMonad($fn($value), $log); }; }
その後、loggify ラッパーを次のように使用できます。
function add2(int $v): int { return $v + 2; } function square(int $v): int { return $v * $v; } function multi3(int $v): int { return $v * 3; } function logIt($value, callable ...$fns) { $logging_fns = array_map(loggify(...), $fns); $monad = new LoggerMonad($value); foreach ($logging_fns as $fn) { $monad = $monad->bind($fn); } return $monad; } print_r(logIt( 3, add2(...), square(...), multi3(...) ));
出典: Gina Banyard (fr) による「Monades simplement」
??ベイビー、私を傷つけないでください
モナドは、オブジェクトや関数を含む任意の型の値をラップすることを目的としています。
他のラッピング システムと同様に、この値を入力として受け取るコンストラクター (~ クラス) と、実装しようとしているパターンに従って独自の目的を持つメソッドがいくつかあります。
ただし、すべてのモナドにはバインド関数が含まれています。名前が示すように、ここで値 (またはコールバック) が渡されます。
これらのコールバックで何が起こっても、モナドはそれをラップします。これは、値を装飾し、コードをリファクタリングするための強力な方法であると思われます。
明らかに実装に依存しており、最初は迷いやすいです。
ただし、この代替アプローチでは、if ブロックの量を大幅に減らし、戻り値の一貫性を高めることができます。
final class LoggerMonad { public function __construct( public mixed $data, public array $logs = [], ) {} public function bind(callable $fn) { $resultLoggerMonad = $fn($this->data); return new LoggerMonad( $resultLoggerMonad->data, [...$this->logs, ...$resultLoggerMonad->logs], ); } } function loggify(callable $fn): Closure { return function ($value) use ($fn) { $name = (new ReflectionFunction($fn))->name; $log = [ 'Running '. $name .'('. var_export($value, true) .')' ]; return new LoggerMonad($fn($value), $log); }; }
ソース: fp4php - モナド
PHP モナドについて少しは理解できたと思います。
もちろん、目的のためだけに派手なデザインパターンをプロジェクトに追加すべきではありません。
さらに、まったく新しいパラダイムであるにもかかわらず、要点を見逃して、エラー処理などの非常に特殊な側面に焦点を当ててしまうことがよくあります。
それでも、新しいアプローチを発見するのは新鮮です。私たちは既成概念にとらわれずに考える必要があります。
以上がPHP: モナドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。