web-dev-qa-db-ja.com

関数型プログラミングは問題と解決策の間の「表現上のギャップ」を増やしますか?

機械語(たとえば、0110101000110101)のコンピューター言語は一般に、より高度な形式の抽象化に進化しており、問題に適用したときにコードを理解しやすくしています。アセンブラはマシンコードを抽象化したもの、Cはアセンブラを抽象化したものなどです。

オブジェクト指向の設計は、オブジェクトの観点から問題をモデル化できる点で非常に優れているようです。たとえば、大学のコース登録システムの問題は、Courseクラス、Studentクラスなどでモデル化できます。次に、 OO言語での解決策には、責任を負う同様のクラスがあり、これは一般に設計、特にコードのモジュール化に役立ちます。この問題を10の独立したチームに与えて、 OOメソッド、通常、10のソリューションには、共通して問題に関連するクラスがあります。これらのクラスの結合と相互作用に取り掛かると、多くの違いが生じる可能性があるため、 「ゼロの表現ギャップ」のようなもの。

関数型プログラミングの私の経験は非常に限られています(実際の使用はなく、Hello Worldタイプのプログラムのみ)。 FP問題の解決策(表現のギャップが少ない)の方法でOO言語が行う方法と同じように、簡単にマッピングできるようにする方法を確認できていません。

同時プログラミングに関するFPの利点を理解しています。しかし、何かが足りないのですか、それともFPは表現のギャップを減らすことではありません理解する)?

これを尋ねる別の方法:同じ現実世界の問題を解決する10の異なるチームのFPコードは、共通点が多いでしょうか?


ウィキペディアから 抽象化(コンピューターサイエンス) (私の強調):

関数型プログラミング言語は一般に、lambda abstractionsのような、関数に関連する抽象化を示します(項をある変数の関数にする)、高階関数(パラメーターは関数)、括弧abstraction(項を変数の関数にする)。

現実世界の問題はそのような抽象化では簡単にモデル化できないため、表現上のギャップが増える可能性があります。


表現のギャップを減らす別の方法は、ソリューションの要素を問題まで追跡することです。マシンコードの01sは、トレースバックが非常に難しいのに対し、Studentクラスは簡単にトレースバックできます。すべてのOOクラスが問題空間をたどるのは簡単ではありませんが、多くの場合はそうです。

FP抽象化では、解決している問題空間のどの部分を見つけるために常に説明が必要ではないですか(math問題以外))? OK-私はこの部分で良いです。さらに多くの例を見た後、FP抽象化が、データ処理で表現される問題の部分について非常に明確であることがわかります。


関連する質問への受け入れられた回答 MLを使用して関数型プログラムをモデル化できますか? -「関数型プログラマーはダイアグラムをあまり使用しません」と述べています。それがUMLであるかどうかは本当に気になりませんが、FP抽象化が理解/伝達しやすく、広く使用されている図がない場合(この答えが正しいと仮定)繰り返しますが、私のFP使用/理解のレベルは取るに足らないものであるため、単純なFPプログラムの図表は必要ありません。

OO設計には、機能/クラス/パッケージレベルの抽象化があり、それぞれにカプセル化(アクセス制御、情報の隠蔽)があるため、複雑な管理が容易になります。これらは、問題からソリューションへの移行を容易にする要素です。


FPでオブジェクト指向に類似した方法で分析と設計がどのように行われるかについて多くの回答が述べていますが、これまでのところ高レベルのものは誰も引用していません(ポールは興味深いものを引用しましたが、低レベルです) 。私は昨日多くのグーグルを行い、いくつかの興味深い議論を見つけました。以下は Simon Thompsonによる関数型プログラムのリファクタリング(2004) (私の強調)

オブジェクト指向システムの設計では、設計がプログラミングに先行することは当然です。デザインは、EclipseなどのツールでサポートされているUMLのようなシステムを使用して記述されます。初心者のプログラマーは、BlueJのようなシステムを使用してビジュアルデザインアプローチをよく学ぶかもしれません。関数型プログラミングの同様の方法論に関する作業は FAD:関数分析と設計 で報告されていますが、他の作業はほとんどありません。これにはいくつかの理由が考えられます。

  • 既存の関数型プログラムは、設計を必要としない規模のものです。多くの関数型プログラムは小規模ですが、グラスゴーハスケルコンパイラなどのその他のプログラムは充実しています。

  • 関数型プログラムはアプリケーションドメインを直接モデル化するため、デザインは無関係になります。 関数型言語はさまざまな強力な抽象化を提供しますが、これらが現実の世界のモデル化に必要な抽象化をすべて提供するだけであると主張することは困難です。

  • 関数型プログラムは、進化する一連のプロトタイプとして構築されます。

上記の博士論文 では、パラダイムとは関係なく、分析および設計方法論(ADM)を使用する利点について概説しています。しかし、ADMは実装パラダイムと整合する必要があるという主張がなされています。つまり、OOADMはOOプログラミングに最適であり、FPなどの別のパラダイムにはあまり適用されません。ここに、私が表現ギャップと呼んでいるものを言い換えると、すばらしい引用があります。

どのパラダイムがソフトウェア開発に最適なサポートを提供するかについて詳細に議論することができますが、問題の説明から実装および配信までの単一のパラダイムの範囲内に留まる場合、最も自然で効率的で効果的な開発パッケージを実現できます。

FADが提案する一連の図は次のとおりです。

  • 関数の実装で使用する関数を示す関数依存図。
  • 型に同じサービスを提供する型依存図。そして、
  • システムのモジュールアーキテクチャのビューを示すモジュール依存関係図。

FAD論文のセクション5.1に事例研究があります。これは、フットボール(サッカー)リーグに関連するデータの生成を自動化するシステムです。要件は100%機能的です。たとえば、サッカーの結果を入力し、リーグテーブル、スコアリングテーブル、出席表を作成し、チーム間でプレーヤーを移動し、新しい結果の後でデータを更新します。FADが非機能的要件を解決するためにどのように機能するかについては記載されていません。 、「新しい機能は最小限のコストで許可されるべきである」と述べることとは別に、テストすることはほとんど不可能です。

悲しいことに、FADを除いて、FPに提案されているモデリング言語(ビジュアル)の最新のリファレンスはありません。 UMLは別のパラダイムなので、それを忘れてください。

18
Fuhrmanator

基本データは、ほとんどすべてのパラダイムで同じように構造化されています。オブジェクト、構造体、レコードなど、StudentCourseなどが含まれます。 OOPとの違いは、データの構造ではなく、関数の構造です。

実際のところ、関数型プログラムの方が問題に対する考え方と非常によく一致しています。たとえば、次の学期の学生のスケジュールを計画するには、学生が完了したコースのリスト、学生の学位プログラムのコース、この学期に提供されたコース、学生が前提条件を完了したコース、時間のないコースなどを考えます。競合しないなど.

突然、どのクラスがこれらすべてのリストを作成して保存する必要があるのか​​はそれほど明確ではありません。これらのリストの複雑な組み合わせがある場合はさらに少なくなります。ただし、必須クラスを1つ選択する必要があります。

FPでは、学生とコースのリストを受け取り、フィルター処理されたコースのリストを返す関数を記述します。そのようなすべての関数をモジュールにまとめることができます。クラスと組み合わせる必要はありません。

つまり、データモデルは、OOPプログラマーがモデルについて考える前に、他の組み合わせで動作する関数を配置する便利な場所を提供する以外に目的のないクラスで汚染される前に、クラス。OOPで常に必要となる奇妙なCourseStudentFilterListまたは同様のクラスはありませんが、最初の設計では決して考えません。

20
Karl Bielefeldt

Javaクラスを数年前に受講したとき、クラス全体に対する解決策を示すことが期待されていたので、人々がどのように考え、どのように問題を論理的に解決するかを知ることができました。解決策を完全に期待しました3つまたは4つの一般的なソリューションに集中する代わりに、30人の学生が30の完全に異なる方法で問題を解決しているのを見ました。

当然、初心者のプログラマーが経験を積むにつれて、彼らは一般的なソフトウェアパターンに触れるようになり、それらのパターンをコードで使用し始め、その後、それらのソリューションはいくつかの最適な戦略の周りに合体します。これらのパターンは、経験豊富な開発者が通信できる技術言語を形成します。

関数型プログラミングを支える技術言語はmathです。したがって、関数型プログラミングを使用して解決するのに最も適した問題は、本質的に数学の問題です。数学では、私はそのような数学については言及していませんあなたは加算や減算のようなビジネスソリューションで見るでしょう。むしろ、私は、Math Overflowで表示される可能性がある数学の種類、またはOrbitzの検索エンジン(LISPで記述されている)で表示される種類の数学について話しています。

このオリエンテーションは、実際のプログラミング問題を解決しようとする関数型プログラマーにとって重要な影響をもたらします。

  1. 関数型プログラミングは命令型より宣言型です。それは主に、コンピュータにどのように行うかではなく、何をすべきかを伝えることに関係しています

  2. オブジェクト指向プログラムは、多くの場合、トップダウンで構築されます。クラス設計が作成され、詳細が入力されます。機能プログラムは、多くの場合、上位レベルの関数に結合される小さな詳細な関数から始めて、ボトムアップで構築されます。

  3. 関数型プログラミングには、複雑なロジックのプロトタイピング、変更や進化が可能な柔軟なプログラムの構築、初期設計が不明確なソフトウェアの構築などの利点があります。

  4. クラス、オブジェクト間のメッセージ、およびソフトウェアパターンはすべてビジネスドメインにマップし、ビジネスインテリジェンスをキャプチャしてドキュメント化する構造を提供するため、オブジェクト指向プログラムはビジネスドメインにより適しています。

  5. すべての実用的なソフトウェアは副作用(I/O)を生成するため、純粋に関数型のプログラミング言語では、数学的に純粋(モナド)のまま、これらの副作用を生成するメカニズムが必要です。

  6. 関数型プログラムは、その数学的性質により、より簡単に証明できます。 Haskellの型システムは、コンパイル時にほとんどのOO言語ではできない)ものを見つけることができます。

等々。コンピューティングにおける多くのことと同様に、オブジェクト指向言語と関数型言語には異なるトレードオフがあります。

いくつかの最新のOO言語は、便利な関数型プログラミングの概念の一部を採用しているため、両方の長所を活かすことができます。Linq、およびそれをサポートするために追加された言語機能は、その例。

10
Robert Harvey

私が重要だと思う、他の回答ではカバーされていない側面を強調したいと思います。

まず第一に、私は問題と解決策の間の表現上のギャップは、彼らの背景と彼らがより慣れている概念に従って、プログラマーの心の中にもっとあると思うと思います。

OOPとFPは、Robert Harveyがすでに指摘したように、2つの異なる視点からデータと操作を見て、異なるトレードオフを提供します。

それらが異なる重要な側面の1つは、ソフトウェアを拡張する方法です。

たとえば、さまざまな画像形式があり、画像を処理するアルゴリズムのライブラリを維持している場合など、データ型のコレクションと操作のコレクションがある状況を考えます。

関数型プログラミングを使用すると、ソフトウェアに新しい操作を簡単に追加できます。コードをローカルで変更するだけで済みます。つまり、さまざまな入力データ形式を処理する新しい関数を追加するだけです。一方、新しいフォーマットを追加することはより複雑です。すでに実装したすべての関数を変更する必要があります(ローカルでは変更しません)。

オブジェクト指向のアプローチはこれと対称的です。各データ型はすべての操作の独自の実装を持ち、実行時に適切な実装を選択する責任があります(動的ディスパッチ)。これにより、新しいデータ型(新しい画像形式など)を簡単に追加できます。新しいクラスを追加し、そのすべてのメソッドを実装するだけです。一方、新しい操作を追加するとは、その操作を提供する必要のあるすべてのクラスを変更することを意味します。多くの言語では、これはインターフェースを拡張し、それを実装するすべてのクラスを適応させることによって行われます。

この拡張への異なるアプローチが、OOPが、これらの操作が機能するデータ型のセットよりも操作のセットの変化が少ない特定の問題に適している理由の1つです。典型的な例GUIです。すべてのウィジェットが実装する必要のある一連の固定操作(Paintresizemoveなど)と、必要なウィジェットのコレクションがあります。延長。

したがって、この次元によれば、OOPおよびFPは、コードを整理する2つの鏡面反射の方法にすぎません。 [〜#〜] sicp [を参照してください。 〜#〜] 、特にセクション2.4.3、表2.22、および段落メッセージの受け渡し

まとめ:OOPデータを使用して操作を整理する、FP操作を使用してデータを整理する。どちらのアプローチも、状況に応じて強めたり弱めたりします。一般に、2つはどちらも問題と解決策の間に表現上のギャップがありません。

7
Giorgio

ほとんどの関数型言語はオブジェクト指向ではありません。これは、それらにオブジェクトがないことを意味するわけではありません(特定の機能が関連付けられている複合型の意味で)。 Haskellは、Javaと同様に、リスト、マップ、配列、あらゆる種類のツリー、および他の多くの複合型を持っています。 Haskell ListまたはMapモジュールを見ると、ほとんどの同等のOO言語ライブラリモジュールのメソッドに非常に類似した一連の関数が表示されます。コードを検査すると、いくつかのタイプ(または正確にはそのコンストラクター)とモジュール内の他の関数のみが使用できる関数を使用した同様のカプセル化も見つかるでしょう。

Haskellでこれらの型を処理する方法は、OOの方法とほとんど同じです。Haskellでは

  null xs
  length xs

そしてJavaあなたは言う

  xs.isEmpty
  xs.length

トマト、トマト。 Haskell関数はオブジェクトに関連付けられていませんが、そのタイプと密接に関連しています。 ¨ああ、but、¨あなたは言う、¨inJava呼び出しているlengthメソッドは、そのオブジェクトの実際のクラスに適しています¨サプライズ-Haskellは polymorphism でも type classes (およびその他のもの)を実行します。

Haskellでは、is-整数のコレクション-コレクションがリスト、セット、配列のいずれであるかは関係ありません。このコード

  fmap (* 2) is

すべての要素が2倍になったコレクションを返します。ポリモーフィズムとは、特定のタイプに適切なmap関数が呼び出されることを意味します。

これらの例は、実際には本当に複雑なタイプではありませんが、同じことがより困難な問題にも当てはまります。関数型言語では複雑なオブジェクトをモデル化して特定の機能をそれらに関連付けることができないという考えは、単に間違っています。

機能的なスタイルとOOスタイルの間には重要な重要な違いがありますareがありますが、この答えはそれらに対処する必要はないと思います。関数型言語は、問題とタスクの直感的なモデリングを妨げます(または妨げます)。

5
itsbruce

FPは確かに、代表格差の縮小に努めています。

関数型言語でよく目にするのは、言語を(ボトムアップ設計を使用して) Embedded Domain Specific Language(EDSL) に構築する方法です。これにより、プログラミング言語内でドメインにとって自然な方法でビジネス上の懸念を表現する手段を開発できます。 HaskellとLISPはどちらもこのことに誇りを持っています。

この能力を可能にするものの一部(すべてではないにしても)は、ベース言語自体の中での柔軟性と表現力です。ファーストクラス関数、高次関数、関数構成、および一部の言語では 代数的データ型 (区別された共用体)と演算子を定義する機能*により、表現方法の柔軟性が向上しますOOPの場合よりも、つまり、問題ドメインから物事を表現するより自然な方法が見つかる可能性があります。 (多くのOOP言語は、これらの機能を最初から採用していなければ、最近これらの機能の多くを採用しています。)

*はい、多くのOOP言語では標準演算子をオーバーライドできますが、Haskellでは新しい演算子を定義できます!

関数型言語(主にF#とHaskell、一部のClojure、および少しのLISPとErlang)での作業経験では、OOP =-特に代数的データ型の場合、クラスよりも柔軟だと思います。FP、特にHaskellを使い始めたときにループが発生した理由の1つは、以前よりも少し高いレベルで考えたり作業したりする必要があったことです。命令型またはOOP言語で使用されていました。これも少し実行されている可能性があります。

4
paul

Niklaus Wirthが言うように、「アルゴリズム+データ構造=プログラム」。関数型プログラミングはアルゴリズムを整理する方法に関するものであり、データ構造を整理する方法についてはあまり説明していません。確かに、FP可変(LISP)と不変(Haskell、Erlang)の両方の変数を持つ言語が存在します。FPを何かと比較して対比したい場合は、命令型(C、Java)および宣言型(Prolog)プログラミングを選択する必要があります。

一方、OOPはデータ構造を構築し、それらにアルゴリズムをアタッチする方法です。 FPは、OOP onesと同様のデータ構造を構築することを妨げるものではありません。私を信じていない場合は、Haskellの型宣言をご覧ください。ただし、ほとんどのFP言語は、関数がデータ構造に属しておらず、同じ名前空間にあるべきであることに同意します。これは、関数構成による新しいアルゴリズムの構築を簡素化するために行われます。実際に、関数が取る入力と生成する出力、なぜそれが属しているデータ構造も重要なのでしょうか?たとえば、add関数を整数型のインスタンスで呼び出して、単純にではなく引数を渡す必要があるのはなぜですか? 2つの整数引数が渡されますか?

したがって、FPがソリューションを一般的に理解しにくくする理由はわかりません。とにかくそれがそうだとは思いません。しかし、熟練した命令型プログラマは確かに関数型プログラムを命令型プログラムよりも理解するのが難しい、またはその逆です。これは、Windows-Linuxの二分法に似ています。Windows環境に慣れるために10年を費やした人が、1か月の使用後にLinuxが難しいことに気づいた場合、およびLinuxはWindowsで同じ生産性を達成できないため、あなたが話している「表現のギャップ」は非常に主観的です。

3
mkalkov