1. ホーム
  2. oop

[解決済み】関数型プログラミングはGoFデザインパターンに取って代わるか?

2022-03-23 23:37:02

質問

学習を始めてから F# OCaml 昨年、私は、デザインパターン(特にJava)は命令型言語に欠けている機能の回避策であると主張する膨大な数の記事を読みました。私が見つけた記事のひとつは は、かなり強い主張をしています。 :

私が出会ったほとんどの人は デザインパターンの本 ギャング・オブ 4(GoF)です。自尊心のあるプログラマーなら この本は 言語にとらわれず、パターン ソフトウェアエンジニアリングに適用される どの言語であっても、一般的に を使用します。これは立派な主張である。 しかし、残念ながら、これは 真実です。

関数型言語は非常に の表現力です。 関数型言語では デザインパターンは必要ない というのも、この言語は非常に高度である可能性が高いからです。 でプログラミングすることになります。 設計を排除する概念 のパターンがあります。

関数型プログラミング(FP)の主な特徴として、ファーストクラスの値としての関数、currying、immutableな値などが挙げられます。OOデザインパターンがこれらの機能のいずれかに近似していることは、私には明らかでないように思えます。

さらに、F#やOCamlなどのOOPをサポートする関数型言語では、これらの言語を使用するプログラマが、他のすべてのOOP言語で利用可能なものと同じデザインパターンを使用することは明らかだと思われます。実際、私は毎日F#とOCamlを使っていますが、これらの言語で使うパターンと、Javaで書くときに使うパターンとの間に、顕著な違いはありません。

関数型プログラミングはOOPデザインパターンを不要にするという主張は本当でしょうか?もしそうなら、典型的なOOPデザインパターンとその関数的等価物の例を掲載するか、リンクを貼っていただけませんか?

どのように解決するのですか?

引用されたブログ記事は、少し主張を誇張していますね。FPは 排除する デザインパターンの必要性 デザインパターンという言葉は、FP言語において同じことを説明するために広く使われていないだけなのです。しかし、それらは存在する。関数型言語には、「問題Xに遭遇したら、Yのようなコードを使え」という形のベストプラクティスルールがたくさんあります。

しかし、OOP特有のデザインパターンは、関数型言語ではほとんど意味をなさないというのが正しいでしょう。

デザインパターンが、そのようなものであるということは、特に議論の余地はないと思います。 一般に は、言語の欠点を補うためにのみ存在します。 そして、もし他の言語が同じ問題を些細なことで解決できるのであれば、その他の言語にはそのためのデザインパターンは必要ないだろう。その言語のユーザーは、この問題には気づかないかもしれません。 存在する なぜなら、その言語では問題にならないからです。

この問題についてのGang of Fourの意見は以下の通りです。

<ブロッククオート

プログラミング言語の選択は、その人の視点を左右する重要なものです。私たちのパターンはSmalltalk/C++レベルの言語機能を想定しており、その選択によって、何が簡単に実装できて、何ができないかが決まります。もし、手続き型言語を想定していれば、継承、カプセル化、ポリモーフィズムというデザインパターンが含まれていたかもしれません。同様に、私たちのパターンのいくつかは、あまり一般的でないオブジェクト指向言語によって直接サポートされています。例えば、CLOSにはマルチメソッドがあり、Visitorのようなパターンの必要性が低くなっています。実際、SmalltalkとC++の間には十分な違いがあり、一部のパターンは一方の言語で他方よりも簡単に表現できるようになっています。(例えば、Iteratorを参照してください)。

(以上、「デザインパターン入門」4ページ3段落目より引用)

<ブロッククオート

関数型の主な特徴 プログラミングには、関数を ファーストクラスの値、カリー 不変の値、など。それは OOデザインパターンは は、これらのいずれにも近似している。 の機能です。

第一級関数の近似でなくて、コマンドパターンとは何でしょうか :) FP言語では、単に関数を引数として別の関数に渡すだけです。 OOP言語では、関数をクラスでラップしてインスタンス化し、そのオブジェクトを他の関数に渡す必要があります。効果は同じですが、OOPではデザインパターンと呼ばれ、より多くのコードを必要とします。 そして、抽象ファクトリーパターンとは、カレーでなければ何でしょうか?関数に少しずつパラメータを渡して、最終的に呼び出すときにどのような値を吐き出すかを設定するのです。

そう、GoFのデザインパターンのいくつかは、FP言語では冗長になっているのです。

しかし、もちろん、まだデザインパターンはあります。 ではない FP言語では解決できない。FPのシングルトンに相当するものは何でしょうか? (シングルトンは一般に使うにはひどいパターンだということはちょっと無視してください)。

そして、それは両方向に作用するのです。先ほども言ったように、FPにもデザインパターンがありますが、人々は通常そのように考えないだけです。

でも、モナドに出会ったことがあるかもしれませんね。これがグローバルな状態を扱うためのデザインパターンでないとしたら、いったい何なのでしょう?これは、OOP言語では非常に単純な問題で、同等のデザインパターンは存在しないのです。

スタティック変数をインクリメントしたり、ソケットから読み込んだりするためのデザインパターンは必要ないのです。 する .

モナドがデザインパターンだと言うのは、通常の演算とゼロの要素を持つ整数がデザインパターンだと言うのと同じくらいばかげた話です。 いや、モナドというのは 数学的パターン デザインパターンではありません。

純粋な)関数型言語では、モナドのデザインパターンや他の同じことを可能にする方法で回避しない限り、副作用やミュータブルステートは不可能です。

<ブロッククオート

さらに、関数型言語では OOPをサポートする(F#や OCaml)には、明らかに これらの言語を使用するプログラマは 同じデザインパターンを使用します。 他のすべてのOOPで利用可能であることが判明 言語です。実際、私は今、F#を使用しています。 とOCamlを毎日使っていますが、全く問題ありません。 との間に顕著な違いがあります。 これらの言語で使用するパターンと で書くときに使うパターン Javaです。

おそらく、まだ命令形で考えているからではないでしょうか?多くの人は、ずっと命令型言語と付き合ってきて、関数型言語を試したときに、その習慣を捨てられないでいます。(私は、F#でかなりおかしな試みを見てきました。 すべての 関数は'let'文の羅列で、基本的にはC言語のプログラムを作成し、セミコロンをすべて'let'に置き換えたようなものです:))

しかし、もうひとつの可能性は、OOP言語のデザインパターンが必要な問題を些細なことで解決していることに気づいていないだけかもしれません。

カレーを使ったり、関数を別の関数に引数として渡したりするときは、一度立ち止まって、OOP言語ではどうやるのか考えてみてください。

<ブロッククオート

という主張は本当なのでしょうか? 関数型プログラミングは OOPデザインパターンが必要ですか?

うん、そうだね :) FP言語で作業する場合、OOP特有のデザインパターンはもはや必要ありません。しかし、MVCやその他の非OOP固有のもののような一般的なデザインパターンはまだ必要で、代わりに新しいFP固有の"デザインパターン"が2つほど必要です。どの言語にも欠点があり、デザインパターンはそれを回避するための手段なのです。

とにかく、次のような「よりクリーンな」FP言語に挑戦してみるのも面白いかもしれません。 ML (少なくとも学習目的では、個人的にお気に入りです)、または ハスケル 新しいことに直面したときに、OOPという松葉杖に頼る必要はないのです。


予想通り、私がデザインパターンを「言語の欠点を補うもの」と定義したことに対して、何人かの方から反論がありましたので、以下に私の正当性を説明します。

すでに述べたように、ほとんどのデザインパターンは、あるプログラミングパラダイム、あるいは、ある特定の言語に特化したものです。多くの場合、デザインパターンは ある そのパラダイムの中で(FPのモナド、OOPの抽象ファクトリを参照)。

なぜFPに抽象ファクトリーパターンが存在しないのか?それは、それが解決しようとする問題がそこに存在しないからです。

つまり、もしOOP言語には存在し、FP言語には存在しない問題があるとすれば、それは明らかにOOP言語の欠点と言えるでしょう。その問題は解決できるのですが、その言語では解決できず、その問題を回避するために多くの定型的なコードを要求されるのです。理想を言えば、私たちのプログラミング言語が魔法のように すべて の問題が解消されます。まだ残っている問題は、原則的にその言語の欠点です ;)