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


26

私がより機能的なプログラミングをすればするほど、タマネギのレイヤーが以前のレイヤーをすべて包含しているように見える、抽象化のレイヤーが追加されたように感じます。

私はこれが本当かどうかわからないので、私が長年取り組んできたOOP原則から外れると、誰もそれらがどれほど正確に機能するかを説明できます:カプセル化、抽象化、継承、多態性

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

Haskellが「インターフェース」の要件を満たすことができることは知っていますが、メソッドが機能するかどうかは確かではありませんか?ファンクターが数学的な基礎を持っているという事実は、おそらく機能的な期待に基づいて構築されていると思いますか?

機能的にOOPの4つの原則を満たしている、または満たしていないと思う方法を詳しく説明してください。

編集:私は機能的パラダイムとオブジェクト指向パラダイムの違いをうまく理解しており、最近では両方を実行できるマルチパラダイム言語がたくさんあることを認識しています。私は本当に、完全なfp(haskellのような純粋主義者と考えてください)がリストされた4つのことのどれを行うことができるか、またはなぜそれらのいずれもできないのかの定義を探しています。すなわち、「カプセル化はクロージャを使用して行うことができます」(またはこの信念に誤りがある場合は、理由を述べてください)。


7
これらの4つの原則は、OOPを「作成」しません。OOPは、クラス、クラス階層、およびそれらのインスタンスを使用して、それらを単に「解決」します。しかし、関数型プログラミングでそれらを達成する方法があれば、私も答えが欲しいです。
陶酔

2
@Euphoricは、定義によっては、メイクし OOPを。
コンラッドルドルフ

2
@KonradRudolph私は多くの人々がこれらのこととそれらがもたらす利益をOOPのユニークな特性として主張していることを知っています。「ポリモーフィズム」が「サブタイプポリモーフィズム」を意味すると仮定すると、後者2つはOOPに不可欠です。しかし、明らかに非OOPアプローチを除外するカプセル化と抽象化の有用な定義にまだ出会っていません。Haskellでも、詳細を非表示にしてアクセスを制限できます。また、Haskellには、サブタイプの多型ではなく、アドホックな多型もあります。問題は、「サブタイプ」ビットが重要なのでしょうか?

1
@KonradRudolphそれはもう受け入れられません。どちらかといえば、それをステップアップし、それを広める人々にそれを再考する理由を与えるインセンティブです。

1
抽象化は、すべてのプログラミング、少なくとも生のマシンコードを超えるプログラミングに固有です。カプセル化はOOPよりもずっと前からあり、関数型プログラミングに固有のものです。関数型言語は、継承またはポリモーフィズムの明示的な構文を含める必要はありません。合計すると「いいえ」になると思います。
-sdenham

回答:


44

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

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

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


1
まあ、継承は(それが必要な非常にまれなケースで)合成よりも達成可能であり、型レベルの継承よりもクリーンです。多型は、特に多型が存在する場合に自然です。しかし、もちろん、FPはOOPとその原則とは関係がないことに同意します。
SKロジック

偽物を作成することは常に可能です。選択した言語でオブジェクトを実装できます。しかし、私は他のすべてに同意します:)
エリオットボール

5
「副作用」という用語は、機能的なプログラマーによって作られた(または主に使用された)とは思いません。
sepp2k

4
@ sepp2k:彼は彼らが用語を発明したとは言わず、芝生から降りることを拒否する子供を指すのに通常使用するのとほぼ同じトーンを使ってそれを振る舞うだけだ。
アーロンノート

16
@Aaronaughtそれは私を気にするのは私の芝生の子供たちではなく、それは彼らの血まみれの副作用です!彼らが私の芝生全体で変異するのをやめるなら、私はそれらを全く気にしません。
ジミー・ホッファ

10

OOPとFPを比較するには、次の直観が役立つと思います。

FPをOOPのスーパーセットと見なすのではなく、OOPとFPを、同様の基礎となる計算モデルを見る2つの代替方法と考えてください。

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

OOPでは、これは

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

FPでは、これは

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

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

さまざまな視点は、オブジェクト指向ビューがオブジェクト(データ)を中心とし、機能ビューが関数/クロージャー(操作)を中心とするという事実に起因すると思います。


8

OOPの定義を誰に依頼するかによります。5人に尋ねると、おそらく6つの定義が得られます。ウィキペディアによると

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

だから誰かが非常に決定的な答えを出すときはいつでも、一粒の塩でそれを取る。

そうは言っても、FP パラダイムとしてのOOPのスーパーセットであるという良い議論があります。特に、オブジェクト指向プログラミングという用語の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の意味のある分類ですか?関数型プログラミングのサブセットと考えると便利ですか?ほとんどの場合、そうではないと思います。


1
パラダイムを切り替えたときに、どこに線を引くべきかを考えることは、意味があるかもしれません。fpでサブタイプのポリモーフィズムを達成する方法がないと言われた場合、それとうまく合うものをモデリングする際にfpを使用しようとすることはありません。しかし、それが可能であれば、fpスペースで頻繁に作業しているが、いくつかのニッチスペースでサブタイプのポリモーフィズムを望んでいるときに、良い方法を達成するのに時間がかかるかもしれません(良い方法は不可能かもしれません)。明らかに、システムの大部分がそれに適合する場合、OOPを使用する方が良いでしょう。
ジミー・ホッファ

6

OOのようなFPは、明確に定義された用語ではありません。異なる、時には矛盾する定義を持つ学校があります。それらの共通点を理解すると、次のことがわかります。

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

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

他のすべては他の場所に存在するか、場合によっては存在しません。

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


「一次関数」ではなく「一次関数」を意味すると思います。
dan_waterworth

エラー... C ++ 11ラムダは、ほとんどファーストクラスの関数ではありません。各ラムダは、ネイティブ関数ポインタ型と互換性のない独自のアドホック型(すべての実用的な目的で、匿名構造体)を持っています。またstd::function、関数ポインタとラムダの両方を割り当てることができるオブジェクト指向ではなく、明らかに汎用です。オブジェクト指向の限られたブランドのポリモーフィズム(サブタイプポリモーフィズム)は、パラメトリックポリモーフィズム(完全なシステムFオメガは言うまでもなく、Hindley-Milnerでさえ)より厳密に強力ではないため、これは驚くことではありません。
ピオン

私は純粋な関数型言語の豊富な経験はありませんが、クロージャー内で1つの静的メソッドのクラスを定義し、それらを異なるコンテキストに渡すことができれば、少なくとも途中で(厄介なことに)いると思います機能スタイルのオプションがあります。ほとんどの言語で厳密なパラメータを回避する方法はたくさんあります。
エリックReppen

2

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

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


3
不変クラスは、高階関数、リスト内包表記、またはクロージャーを作成しません。Fpはサブセットではありません。
ジミー・ホッファ

1
@Jimmy Hoffa:同様のタイプのオブジェクトを取得し、この同様のタイプのオブジェクトを返す単一のメソッドを持つクラスを作成することにより、より高いoreder関数を簡単にシミュレートできます(メソッドを持ちフィールドを持たないタイプ) 。リストの理解は、パラダイムではなくプログラミング言語に関連するものではありません(Smalltalkはそれをサポートしており、OOPです)。クロージャーはC#に存在し、Javaにも挿入されます。
m3th0dman

はい、C#にはクロージャーがありますが、それはマルチパラダイムであるため、クロージャーは他のfpピースとともにC#に追加されました(私は永遠に感謝しています)が、oop言語での存在はそれらをoopにしません。ただし、高階関数についての良い点は、クラスにメソッドをカプセル化すると同じ動作が可能になることです。
ジミー・ホッファ

2
ええ、しかし、状態を変更するためにクロージャを使用する場合、あなたはまだ機能的なパラダイムでプログラムしますか?ポイントは-機能的パラダイムは、高次関数、再帰、またはクロージャではなく、状態の欠如に関するものです。
m3th0dman

fpの定義に関する興味深い考え。これについては、観測結果を共有してくれてありがとう。
ジミーホッファ

2

回答:

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

あるパラダイムが他のパラダイムのスーパーセットであるとは思わない。それらはプログラミングに関する異なる観点であり、いくつかの問題はある観点からよりよく解決され、いくつかは別の観点からよりよく解決されます。

あなたの質問は、FPとOOPのすべての実装によってさらに複雑になります。各言語には、質問に対する適切な回答に関連する独自の癖があります。

ますます接線のとりとめ:

Scalaのような言語は、両方の長所を提供しようとするという考えが気に入っています。私はそれがあなたに両方の世界の合併症を与えることを心配しています。

Javaはオブジェクト指向言語ですが、バージョン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つは、インターフェイスを宣言し、さまざまな機能とデータでそのインターフェイスを実装できることです。また、いくつかの可変データを処理したいので、すべてのプログラム設計で可変性と副作用を制限しようとしても、関数型言語だけでは完全に快適ではないと思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.