web-dev-qa-db-ja.com

関数型プログラミングはオブジェクト指向のスーパーセットですか?

私が行う関数型プログラミングが多ければ多いほど、タマネギのレイヤーがすべて前のレイヤーを取り囲んでいるように見える、抽象化の余分なレイヤーを追加するように感じます。

これが本当かどうかわからないので、OOP私が長年取り組んできた原則から離れて、機能がどのように機能するか、またはそれらを正確に表現していないかを誰かに説明できます:カプセル化、抽象化、継承、ポリモーフィズム

私たちは皆言うことができると思います、はい、それはタプルによるカプセル化を持っていますか、それともタプルは技術的に「関数型プログラミング」の事実として数えますか、それとも単に言語のユーティリティですか?

Haskellが「インターフェース」の要件を満たすことができることは知っていますが、その方法が機能的であるかどうか確信が持てませんか?ファンクタが数学的な根拠を持っているという事実は、おそらくそれらが機能の期待に組み込まれた明確なものであると言えるのではないでしょうか?

ファンクショナルがOOPの4つの原則を満たしているかどうかについて、詳しく説明してください。

編集:私は関数型パラダイムとオブジェクト指向パラダイムの違いをよく理解しており、最近は両方を実行できるマルチパラダイム言語がたくさんあることに気づきました。 私は本当に、あからさまなfp(haskellのような純粋主義者)がリストされた4つのことのどれを実行できるか、またはなぜそれがどれも実行できないかの定義を探しています。つまり、「カプセル化はクロージャで実行できます」(または私がこの信念で間違っている場合は、その理由を説明してください)。

26
Jimmy Hoffa

関数型プログラミングはOOPの上の層ではなく、完全に異なるパラダイムです。関数型でOOPを実行することができます(F#はまさにこの目的のために作成されました)。オブジェクト指向の原則を明示的に拒否するHaskellのようなもののスペクトルの反対側。

モジュールと関数をサポートするのに十分な高度な言語でカプセル化と抽象化を行うことができます。 OOは、カプセル化のための特別なメカニズムを提供しますが、OOに固有のものではありません。 OOのポイントは、あなたが言及した2番目のペア、継承とポリモーフィズムです。この概念は正式にはLiskov置換と呼ばれ、オブジェクト指向プログラミングの言語レベルのサポートなしでは実現できません。 (はい、場合によってはそれを偽ることが可能ですが、OOがテーブルにもたらす多くの利点を失います。)

関数型プログラミングは、リスコフ置換に焦点を合わせていません。抽象化のレベルを上げることに焦点を当て、「副作用」を伴う可変状態とルーチンの使用を最小限に抑えることに焦点を当てています。これは、機能的なプログラマーが実際に何かを(単純に何かを計算するのではなく)音を鳴らすルーチンを作成するために使用する用語です怖い。しかし、繰り返しになりますが、これらは完全に別個のパラダイムであり、プログラマーの言語とスキルに応じて、一緒に使用することもしないこともできます。

45
Mason Wheeler

次の直感は、OOPとFPを比較するのに役立ちます。

FPをOOPのスーパーセットとして考えるのではなく、OOPおよびFPを、あなたが持っている同様の基礎となる計算モデル:

  1. 実行されるいくつかの操作、
  2. 操作へのいくつかの入力引数、
  3. 操作の定義に影響を与える可能性があるいくつかの固定データ/パラメーター、
  4. 一部の結果値、および
  5. おそらく副作用。

OOPこれは

  1. 実行されるメソッド、
  2. メソッドの入力引数、
  3. メソッドが呼び出されるオブジェクト。メンバー変数の形式のローカルデータを含みます。
  4. メソッドの戻り値(おそらくvoid)、
  5. メソッドの副作用。

FPこれは

  1. 実行されるクロージャー、
  2. クロージャの入力引数、
  3. キャプチャーされたクロージャーの変数
  4. クロージャーの戻り値
  5. クロージャーの可能な副作用(Haskellのような純粋な言語では、これは非常に制御された方法で発生します)。

この解釈により、オブジェクトはすべて同じ非ローカル変数(コレクション内のすべてのクロージャーに共通のオブジェクトのメンバー変数)をキャプチャーするクロージャー(そのメソッド)のコレクションと見なすことができます。この見方は、オブジェクト指向言語では、クロージャが1つのメソッドを持つオブジェクトとしてモデル化されることが多いという事実によってもサポートされています。

別の見方は、機能指向のビューが関数/クロージャ(操作)の中心にあるのに対して、オブジェクト指向のビューはオブジェクト(データ)の中心にあるという事実に由来すると思います。

10
Giorgio

それはあなたがOOPの定義を誰に求めるかに依存します。 5人に尋ねると、おそらく6つの定義が得られます。 ウィキペディアは言う

オブジェクトの背後にあるコンセンサスの定義または理論を見つける試みは、あまり成功していないことが証明されています

だから、誰かが非常に明確な答えを出すときはいつでも、塩の粒と一緒にそれをとってください。

つまり、FPOOPパラダイムとして。特に Alan Kayによるオブジェクト指向プログラミングの定義 はこの概念に矛盾しません(しかし Kristen Nygaardのそうです )。すべてがオブジェクトであり、そのロジックはオブジェクト間でメッセージを渡すことによって実装されます。

おそらくあなたの質問にとってより興味深いかもしれませんが、クラスとオブジェクトは、関数によって返される関数とクロージャー(クラスとコンストラクターとして同時に機能する)の観点から考えることができます。これはプロトタイプベースのプログラミングに非常に近く、実際JavaScriptはそれを正確に行うことができます。

var cls = function (x) {
    this.y = x;
    this.fun = function () { alert(this.y); };
    return this;
};

var inst = new cls(42);
inst.fun();

(もちろん、JavaScriptは値の変更を許可しますが、これは純粋に関数型のプログラミングでは違法ですが、OOPの厳密な定義では必要ありません。)

しかし、より重要な質問は次のとおりです:これはOOPの意味のある分類ですか?考えるのに役立ちますか?関数プログラミングのサブセットとして?ほとんどの場合、そうではないと思います。

8
Konrad Rudolph

FPのようなOOは、明確に定義された用語ではありません。異なる、時には矛盾する定義を持つ学校があります。彼らが共通して持っているものを採用すると、次のようになります。

  • 関数型プログラミングはファーストクラスの関数を使ったプログラミングです

  • オブジェクト指向プログラミングは、動的に解決されるオーバーロードの少なくとも制限された形式と組み合わされた 包含ポリモーフィズム を使用したプログラミングです。 (補足:OOの円polymorphismは通常inclusionポリモーフィズム、FP学校は通常parametricポリモーフィズムを意味します。)

それ以外はすべて別の場所に存在するか、場合によっては存在しません。

FPとOOは、2つの抽象化構築ツールです。それらにはそれぞれ長所と短所があります(たとえば、式の問題で異なる優先拡張方向を持っています)が、本質的に他のものより強力なものはありません。 OOカーネル上にFPシステムを構築できます(CLOSはそのようなシステムの1つです)。 OOフレームワークを使用して、ファーストクラスの関数を取得できます(たとえば、C++ 11でラムダ関数を定義する方法を参照してください)。

6
AProgrammer

番号; OOPは手続き型プログラミングのスーパーセットと見なすことができ、インスタンスフィールドで状態が表されるため、関数型パラダイムとは根本的に異なります。関数型パラダイムでは、変数は定数データに適用される関数です望ましい結果を得るために。

実際には、OOPのサブセットである関数型プログラミングを検討できます。すべてのクラスを不変にすると、ある種の関数型プログラミングがあると考えることができます。

2
m3th0dman

回答:

ウィキペディアには関数型プログラミングに関する優れた記事があります あなたが求めるいくつかの例があります。 @Konrad Rudolphはすでに OOP記事 へのリンクを提供しています。

私は、1つのパラダイムが他のパラダイムのスーパーセットであるとは思いません。それらはプログラミングに対する異なる見方であり、いくつかの問題は1つの見方から、いくつかは別の見方から解決されます。

FPおよびOOPのすべての実装により、質問はさらに複雑になります。各言語には、関連する独自の癖がありますあなたの質問に対する良い答えに。

ますます接線方向のランブリング:

Scalaのような言語があなたに両方の世界の最高のものを提供しようとするという考えが好きです。それはあなたに両方の世界の複雑さも与えることを心配しています。

JavaはOO言語ですが、バージョン7は一種のクロージャーを模倣するために使用できる「try-with-resources」機能を追加しました。ここではローカル変数「a」の更新を模倣します別の関数の中間で、その関数からは見えません。この場合、他の関数の前半はClosureTry()コンストラクターで、後半はclose()メソッドです。

public class ClosureTry implements AutoCloseable {

    public static void main(String[] args) {
        int a = 1;
        try(ClosureTry ct = new ClosureTry()) {
            System.out.println("Middle Stuff...");
            a = 2;
        }
        System.out.println("a: " + a);
    }

    public ClosureTry() {
        System.out.println("Start Stuff Goes Here...");
    }

    /** Interface throws exception, but we don't have to. */
    public void close() {
        System.out.println("End Stuff Goes Here...");
    }
}

出力:

Start Stuff Goes Here...
Middle Stuff...
End Stuff Goes Here...
a: 2

これは、ストリームを開いたり、ストリームに書き込んだり、確実に閉じたりするという意図された目的、または2つの関数をペアリングして、2つの関数の間で何らかの作業を行った後に忘れないようにするのに役立つ場合があります。 。もちろん、他のプログラマーが何かを壊していることに気付かずにtryブロックを削除する可能性があるのは非常に新しくて珍しいことなので、現在は一種のアンチパターンですが、実行できるのは興味深いことです。

ほとんどの命令型言語では、ループを再帰として表現できます。オブジェクトと変数は不変にすることができます。副作用を最小限に抑えるためにプロセスを書くことができます(ただし、コンピューターでは真の機能は実現できないと主張します-実行にかかる時間とそれが消費するプロセッサー/ディスク/システムリソースは避けられない副作用です)。一部の関数型言語では、すべてではないにしてもオブジェクト指向の操作を多数実行できます。一部の言語には、特定のパターン(可変フィールドなど)を妨げる制限(変数の更新を許可しないなど)がありますが、相互に排他的である必要はありません。

私にとって、オブジェクト指向プログラミングの最も有用な部分は、データの非表示(カプセル化)、十分に類似したオブジェクトの扱い(ポリモーフィズム)、およびデータとそのデータを一緒に操作するメソッド(オブジェクト/クラス)の収集です。継承はOOPのフラグシップである可能性がありますが、私にとってそれは最も重要でなく、最も使用されていない部分です。

関数型プログラミングの最も有用な部分は、不変性(変数ではなくトークン/値)、関数(副作用なし)、およびクロージャーです。

オブジェクト指向ではないと思いますが、コンピューターサイエンスで最も有用なことの1つは、インターフェイスを宣言してから、さまざまな機能とデータでそのインターフェイスを実装できることです。私はまた、いくつかの変更可能なデータを操作したいので、すべてのプログラム設計で可変性と副作用を制限しようとしていますが、関数型言語だけでは完全に快適ではないと思います。

2
GlenPeterson