web-dev-qa-db-ja.com

関数型プログラミングの落とし穴/欠点

関数型プログラミングを使用したくないときは?あまり得意ではないものは何ですか?

私はパラダイム全体の欠点をもっと探しています。「広く使われていない」や「利用できる良いデバッガがない」などではありません。これらの答えは今のところ正しいかもしれませんが、FPは新しい概念(避けられない問題)であり、固有の性質ではありません。

関連:

67

関数型プログラミングの多くの欠点を考えるのは難しいです。そして再び、私は関数型プログラミングに関する国際会議の元議長です。

主な欠点は、孤立と参入障壁に関係していると思います。書くことを学ぶ良い関数型プログラムは、異なる考え方を学ぶことを意味し、それをうまく行うには時間と労力のかなりの投資が必要です 。教師なしで学ぶことは困難です。これらのプロパティは、いくつかの欠点につながります。

  • 新人によって書かれた機能的なプログラムは、不必要に遅くなる可能性があります。たとえば、Cの新人によって書かれたCプログラムよりも可能性が高くなります。一方、新人によって書かれたC++プログラムは、不必要に遅くなります。 (これらのすべての光沢のある機能...)

    一般に、専門家は高速機能プログラムを書くのに困難はありません。実際、8コアおよび16コアプロセッサ上で最高のパフォーマンスを発揮する並列プログラムの一部は、 Haskell で記述されています。

  • PythonまたはVisual Basic。開始するよりも、関数型プログラミングを開始する人が約束の生産性向上を実現する前にgiveめる可能性が高くなります。本と開発ツール。

  • 話す人が少ない。 Stackoverflowは良い例です。比較的少数のHaskellプログラマーが定期的にサイトを訪れます(ただし、HaskellプログラマーはStackoverflowよりもはるかに古く、確立された独自の活発なフォーラムを持っています)。

    また、機能プログラミングの概念は、Smalltalk、Ruby、C++などの言語の背後にあるオブジェクト指向の概念よりも教えるのが難しく、学ぶのも難しいため、隣人と簡単に話すことができないのも事実です。また、オブジェクト指向コミュニティは何をするのかについて良い説明を開発するのに何年も費やしましたが、機能プログラミングコミュニティは明らかに自分のものが素晴らしいと考えており、説明のために特別なメタファーや語彙を必要としません。 (彼らは間違っています。私はまだ最初の素晴らしい本を待っていますFunctional Design Patterns。)

  • lazy関数型プログラミング(HaskellまたはCleanに適用されますが、MLまたはSchemeまたはClojureには適用されません)のよく知られた欠点は、時間を予測することが非常に難しいことですlazy機能プログラムを評価するためのスペースコスト—専門家でさえもできません。この問題はパラダイムの基本であり、消えることはありません。時間と空間の振る舞いを発見するための優れたツールがあります事後、それらを効果的に使用するには、すでに専門家である必要があります。

46
Norman Ramsey

関数型言語を取り巻くでたらめは、関数型プログラミングの最大の問題だと思います。怒りの中で関数型プログラミングを使い始めたとき、私にとって大きなハードルは、LISPコミュニティによって提唱された高度に進化した議論の多く(たとえば、マクロやホモイコニック構文について)が間違っていた理由を理解することでした。今日、私は多くの人々がパラレルプログラミングに関してHaskellコミュニティにだまされているのを見ます。

実際、その一部を確認するために、このスレッドよりも先を見る必要はありません。

「一般に、専門家は高速機能プログラムを書くのに困難はありません。実際、8コアおよび16コアプロセッサで最もパフォーマンスの高い並列プログラムの一部はHaskellで書かれています。」

このようなステートメントは、並列処理に非常に優れているため、専門家がHaskellを選択している印象を与えるかもしれませんが、実はHaskellのパフォーマンスは悪いです。自分のグループの管理下にあるジャーナルや会議の快適なゾーン内でのみ公開することで、真のピアレビューを回避します。 Haskellは、実世界のパラレル/マルチコア/ HPCでは目に見えません。正確に言うと、パラレルプログラミングが面倒だからです。

具体的には、マルチコアプログラミングの本当の課題は、CPUキャッシュを利用して、コアがデータに飢えないようにすることです。これはHaskellのコンテキストでは対処されていない問題です。 MIT) .NET 4のTPL。この手法を使用して、スケーラブルな高レベルにコンパイルするエレガントな高レベル命令型コードを記述する方法についてのすばらしい説明があります。 2008年の論文のパフォーマンスコード マルチスレッドキャッシュ忘却型アルゴリズムのキャッシュの複雑さ 。最先端のParallel Haskellの研究の 私のレビュー で説明しました。

これは、純粋に機能的なプログラミングのパラダイムに大きな疑問符を残します。これは、時間と空間を抽象化するために支払う価格であり、これは常にこの宣言的パラダイムの背後にある主要な動機でした。

編集: Texas Multicore Technologiesは最近、Haskellがマルチコア並列処理のコンテキストで圧倒されることも発見しました

30
Jon Harrop

関数型プログラミングの大きな欠点の1つは、理論的なレベルでは、ハードウェアやほとんどの命令型言語と一致しないことです。 (これは、明らかな長所の1つであり、どのようにではなく、コンピュータにそれを行わせたいのではなく、whatしたいことを表現できます。)

たとえば、関数型プログラミングでは再帰を多用します。数学の「スタック」は無制限なので、これは純粋なラムダ計算では問題ありません。もちろん、実際のハードウェアでは、スタックは非常に有限です。大規模なデータセットを単純に再帰すると、プログラムがブームになります。ほとんどの関数型言語は、これが起こらないように末尾再帰を最適化しますが、アルゴリズム末尾を再帰的にすると、やや不審なコード体操を強制することができます(たとえば、末尾再帰マップ関数は後方リストを作成するか、差を作成する必要があります)リストなので、非末尾再帰バージョンと比較して正しい順序で通常のマッピングされたリストに戻るには追加の作業が必要です。

(差分リストの提案についてはJared Updikeに感謝します。)

29
Chuck

あなたの言語があなたのプログラムを介して状態/例外の振る舞いに良いメカニズムを提供していない場合(例えば、モナドバインドの構文糖)、状態/例外を含むタスクは面倒になります。 (これらの砂糖を使用しても、一部の人々はFPの状態/例外に対処するのが難しいと感じるかもしれません。)

機能的なイディオムは多くの場合、制御の反転または遅延を大量に実行します。これは、デバッグ(デバッガーを使用)に悪影響を及ぼすことがよくあります。 (これは、FP不変性/参照の透過性によりエラーが発生しにくいため、デバッグの頻度が少なくなることを意味します。

23
Brian

Philip Wadlerはこれについて論文を書いて(誰も関数型プログラミング言語を使用しない理由と呼ばれる)、人々がFP言語を使用できないようにする実際的な落とし穴に対処しました。

更新:ACMアクセスのあるユーザー向けのアクセス不能な古いリンク:

13
Jared Updike

速度や採用の問題やより基本的な問題に対処することは別として、関数型プログラミングでは、既存のデータ型に新しい関数を追加するのは非常に簡単ですが、新しいデータ型を追加するのは「難しい」と聞きました。考慮してください:

(SMLnjで記述されています。また、やや不自然な例はご容赦ください。)

datatype Animal = Dog | Cat;

fun happyNoise(Dog) = "pant pant"
  | happyNoise(Cat) = "purrrr";

fun excitedNoise(Dog) = "bark!"
  | excitedNoise(Cat) = "meow!";

非常に迅速に以下を追加できます。

fun angryNoise(Dog) = "grrrrrr"
  | angryNoise(Cat) = "hisssss";

ただし、Animalに新しいタイプを追加する場合は、各機能を介してサポートを追加する必要があります。

datatype Animal = Dog | Cat | Chicken;

fun happyNoise(Dog) = "pant pant"
  | happyNoise(Cat) = "purrrr"
  | happyNoise(Chicken) = "cluck cluck";

fun excitedNoise(Dog) = "bark!"
  | excitedNoise(Cat) = "meow!"
  | excitedNoise(Chicken) = "cock-a-doodle-doo!";

fun angryNoise(Dog) = "grrrrrr"
  | angryNoise(Cat) = "hisssss"
  | angryNoise(Chicken) = "squaaaawk!";

ただし、オブジェクト指向言語の場合は正反対です。抽象クラスに新しいサブクラスを追加するのは非常に簡単ですが、実装するすべてのサブクラスの抽象クラス/インターフェースに新しい抽象メソッドを追加するのは面倒です。

8
Ben Torell

私は話をしながら今Haskellを学んでいるので、逸話で話題になりたかっただけです。 Haskellを学んでいるのは、アクションから関数を分離するという考え方が私にとって魅力的であり、純粋な関数を非純粋な関数から分離しているため、暗黙的な並列化の背後にある非常にセクシーな理論があるからです。

私は3日間、foldクラスの関数を学習しています。 Foldには、リストを取得して単一の値に減らすという非常に単純なアプリケーションがあるようです。 Haskellはfoldlfoldrを実装しています。 2つの関数の実装は大きく異なります。 _foldl'_と呼ばれるfoldlの代替実装があります。この上に、異なる初期値を持つ_foldr1_および_foldl1_と呼ばれるわずかに異なる構文を持つバージョンがあります。そのうち_foldl1'_に対応する_foldl1_の実装があります。これがすべて気にならないかのように、_fold[lr].*_が引数として必要とし、リダクションで内部的に使用する関数には2つの別個のシグネチャがあり、無限リストで機能するバリアントは1つのみ(r)であり、そのうちの1つだけが定数メモリで実行されます(redexを必要とするだけなので、(L)を理解しています)。 foldrが無限リストで機能する理由を理解するには、少なくとも言語の遅延動作に関する十分な理解と、すべての関数が2番目の引数の評価を強制するわけではないという詳細が必要です。これらの機能のオンライングラフは、大学で見たことのない人にとっては地獄のように混乱しています。 perldocに相当するものはありません。 Haskellプレリュードの関数が何をするのかについての単一の説明は見つかりません。プレリュードは、コアに付属する一種のプリロードされたディストリビューションです。私の最高のリソースは、実際に会ったことのない人(Cale)です。

ああ、foldはリストを非リスト型のスカラーに減らす必要はありません。リストの恒等関数はfoldr (:) [] [1,2,3,4](リストに蓄積できるハイライト)で書くことができます。

/ meは読み取りに戻ります。

3
Evan Carroll

ここに私が遭遇したいくつかの問題があります:

  1. ほとんどの人は、関数型プログラミングを理解するのが難しいと感じています。これは、おそらく機能的なコードを書くのがあなたにとってより困難になることを意味します。
  2. 関数型プログラミング言語は通常、cのような言語よりも低速です。これは時間の経過とともに問題になりつつあります(コンピューターの高速化とコンパイラーの高性能化により)
  3. 命令的なものほど広く普及していないため、一般的なプログラミングの問題のライブラリと例を見つけるのは困難です。 (たとえば、Pythonの場合はほとんど常に簡単に見つけることができますが、Haskellの場合はもっと簡単です)
  4. 特にデバッグ用のツールが不足しています。 Visual Studio for C#やEclipse for Javaを開くほど簡単ではありません。
2
Caleb

関数型プログラミングの特定の実装の詳細から目を離すと、2つの重要な問題があります。

  1. 命令的な問題よりも実際の問題の機能的なモデルを選択することが実際的であるのは比較的まれに思えます。問題領域が必須である場合、その特徴を持つ言語を使用することは自然で合理的な選択です(微妙なバグの数を減らすために、仕様と実装の間の距離を最小限に抑えることが一般的に望ましいため)。はい、これは十分に優れたコーダーによって克服できますが、タスクにロックスターコーダーが必要な場合、それはあまりにも血まみれだからです。

  2. 私が実際に理解したことのない何らかの理由で、関数型プログラミング言語(またはその実装やコミュニティ?)は、言語ですべてのものを持ちたいと思う可能性が非常に高くなります。他の言語で書かれたライブラリの使用ははるかに少ないです。他の誰かが複雑な操作の特に優れた実装を持っている場合は、独自のものを作成する代わりにそれを使用する方がはるかに理にかなっています。これは、外部コードの処理(特に効率的な実行)をかなり困難にする複雑なランタイムの使用の結果であると思われます。私はこの点で間違っていると証明されたいです。

私はこれらの両方が、一般的なコーダーよりもプログラミング研究者によってはるかに強く使用されている関数型プログラミングによって引き起こされるプラグマティズムの一般的な欠如に戻ってくると思います。優れたツールは専門家にすばらしいことを可能にしますが、優れたツールは、普通の人が専門家が普通にできることにアプローチできるようにするものです。

0
Donal Fellows