Pythonで関数型プログラミングができる場合、特定の関数型プログラミング言語が必要ですか?[閉まっている]


22

ジェネレーターとラムダを使用して、Pythonで関数型プログラミングを行うことができます。Rubyでも同じことを実現できます。

質問は次のとおりです。Erlang、Haskell、Schemeなどの特定の関数型プログラミング言語が必要なのはなぜですか?これらの特定の関数型プログラミング言語が提供するものと異なるものはありますか?関数型プログラミングにPythonを使用できないのはなぜですか?


30
これらはすべて、Pythonが作成される前から存在していました
Mahmoud Hossam

51
フォードピントは車です。フェラーリのような特定の高速車が必要なのはなぜですか?
マーティンヨーク

11
クラスとテンプレートを使用して、C ++でオブジェクト指向を行うことができます。なぜJavaとPythonが作成されたのですか?彼らは何を追加しますか?
9000

19
すべてのプログラミング言語(純粋にアカデミックな研究言語を除く)はチューリングと同等であるため、言語Aでできることは他の言語でもできます。したがって、この一連の考え方に従って
Maglob

17
これらの言語のいずれかを知っていれば、Pythonが関数型プログラミングをうまく行うと主張することはないでしょう。そうではありません。FPっぽいものを共有するのに十分ですが、それ以上ではありません。

回答:


20

私は個人的にPythonと関数型プログラミングの両方の大ファンなので、質問に感謝します。Pythonのバックグラウンドが長く、最近Haskellを学び始めたので、機能的な観点から、これらの言語の違いに関する個人的な経験に基づいたいくつかのポイントを示します。

純度

関数の純粋さ(つまり、副作用がないこと)をおもしろいものとして気にしなくても、コードの読みやすさとその理由について実用的な効果があります。独自のPython関数で純度を維持していても、コンパイラに純度を強制させることと、何よりも純度と不変のデータ構造に基づいて構築された標準ライブラリを持つことには大きな違いがあります。

性能

アプリケーションドメインに応じてパフォーマンスを気にする場合としない場合がありますが、静的型付けと保証された純度により、Pythonや他の動的言語と比較して、コンパイラはより多くの作業を行うことができます(PyPyが優れていることは認めざるを得ませんが)そして、例えばLuaJITは奇跡に隣接しています)。

テールコール最適化

パフォーマンスに関連していますが、わずかに異なります。実行時のパフォーマンスをあまり気にしない場合でも、末尾呼び出しの最適化(特に末尾再帰)がないと、スタック制限に達することなくPythonでアルゴリズムを実装する方法が制限されます。

構文

これが、関数型スタイルのPythonを使用するのではなく、「実際の」関数型言語を検討し始めた最大の理由です。Pythonには一般に非常に表現力豊かな構文があると思いますが、関数型コーディングに特有の弱点がいくつかあります。例えば:

  • ラムダ関数の構文はかなり冗長で、含めることができる内容が制限されています
  • 関数構成、つまりf = g . hvs. 用の構文糖f = lambda *arg: g(h(*arg))
  • 部分的なアプリケーション、つまりf = map gvs.f = functools.partial(map, g)
  • すなわち高階関数に挿入演算子を使用するための糖衣構文いいえsum = reduce (+) lstsum = reduce(operator.add, lst)
  • 関数の引数のパターンマッチングやガードがないため、再帰の終了条件やいくつかの境界ケースを非常に読みやすい構文で簡単に表現できます。
  • ブラケットは関数呼び出しでは決してオプションではなく、ネストされた呼び出しに構文上の砂糖はありません。これは好みの問題だと思いますが、特に関数型コードでは、関数呼び出しをチェーンするのが一般的であり、その表記法に慣れると、y = func1 $ func2 $ func3 x読みやすくなることがわかりy = func1(func2(func3(x)))ます。

28

これらは最も重要な違いです:

ハスケル

  • 遅延評価
  • マシンコードにコンパイルします
  • 静的型付けは、関数が純粋であることを保証します
  • 型推論

ハスケルとアーラン

  • パターンマッチング

アーラン

  • 同時実行のアクターモデル、軽量プロセス

スキーム

  • マクロ

すべての言語

  • 実際のクロージャー(Rubyにはクロージャーがあり、Pythonが議論できるかどうか、コメントを参照してください)
  • 関数型プログラミングスタイル(不変コレクション、マップ、フィルター、フォールドなど)に適した標準ライブラリ
  • 末尾再帰(これは一部の非機能言語でも見られます)

また、SML、Ocaml、F#、ScalaのようなMLファミリーの言語を見てください。これは、オブジェクト指向と関数型プログラミングを新しい方法で融合します。これらのすべての言語には、独自の興味深い機能があります。


3
良い投稿を+1。Haskellには型推論を、Erlangには軽量プロセスを追加できます。
ジョナス

1
Pythonには、map、filter、fold(reduce)があります。「本当のクロージャー」について:本当のクロージャーを、単一の式以外のものを含むことができるクロージャーとして定義する場合、Haskellにも本当のクロージャーはありません(もちろん、Haskellには式ではないものがほとんどありません...) 。ただし、不変のデータ構造についてのポイントは良いものです。また、末尾再帰(および一般的に安価な再帰)。
sepp2k

2
「静的型付けは関数が純粋であることを保証する」ための+1。純粋な関数と非純粋な関数を区別する型システムを持つことは非常にクールです。(C ++のconst doe snotはカウントしません。)
Macke

1
@btilly:スコープ内の変数に割り当てることができない場合、それをクロージャとは見なしません。それ以外の場合は、Javaにもクロージャーがあると言わなければなりません。同じトリックを使用できるからです。
キム

3
クロージャーでは、通常と同じ方法で変数にアクセスできます。これはHaskellとRubyのクロージャーには当てはまりますが、PythonやJavaの貧弱な代替物には当てはまりません。他の誰かがアーランについて教えてくれるかもしれませんが、私はそれをよく知りません。
キム

19

「関数型言語」が何であるかを正確に定義することは困難です。リストした言語のうち、純粋に関数型であるのはHaskellのみです(他のすべては何らかのハイブリッドアプローチを採用しています)。ただし、関数型プログラミングに非常に役立つ特定の言語機能があります。また、RubyとPythonには、FPにとって非常に優れた環境となるための十分な機能がありません。重要な順に私の個人的なチェックリストがあります:

  1. ファーストクラスの関数クロージャー(Ruby、Python、およびリストされている他のすべてにはこれがあります)。
  2. テールコールの最適化の保証(Erlang、Haskell、Scala、およびSchemeにはこれがありますが、Python、Ruby、またはClojure(まだ)はありません)。
  3. 言語および標準ライブラリでの不変性のサポート(これは、リストしたすべての「関数型言語」にある大きなものですが(Schemeを除く)、RubyとPythonにはありません)。
  4. 参照透過的な(または純粋な)関数の言語レベルのサポート(私が知る限り、Haskellのみが現在これを持っています)。

(1)の必要性は明白なはずです-高階関数は、一流の関数なしでは非常に困難です。RubyとPythonがFPに適した言語であると人々が話すとき、彼らは通常これについて話します。ただし、この特定の機能は必要ですが、FPに適した言語にするためには十分ではありません。

(2)Schemeが発明されて以来、FPの伝統的な必要性でした。TCOがなければ、スタックオーバーフローが発生するため、FPの基盤の1つである深い再帰でプログラミングすることはできません。これを持たない唯一の「一般的な」機能言語は(JVMの制限のため)Clojureですが、ClojureにはTCOをシミュレートするためのさまざまなハックがあります。(FYI、Ruby TCOは実装固有ですが、Pythonは特にそれをサポートしていません。)TCOが保証されなければならない理由は、実装固有である場合、深い再帰関数がいくつかの実装で壊れてしまうためです。それらをまったく使用します。

(3)は、現代の関数型言語(特にHaskell、Erlang、Clojure、Scala)にある大きなものであり、RubyとPythonにはないものです。あまり詳しく説明しなくても、保証された不変性は、特に同時状況でのバグのクラス全体を排除し、永続的なデータ構造のようきちんとしたものを可能にします。言語レベルのサポートなしでこれらの利点を活用することは非常に困難です。

(4)は、私にとって、純粋に機能的な言語(ハイブリッド言語とは対照的に)について最も興味深いものです。次の非常に単純なRuby関数を考えてみましょう。

def add(a, b)
  a + b
end

これは純粋な関数のように見えますが、演算子のオーバーロードのために、いずれかのパラメーターを変更したり、コンソールへの印刷などの副作用を引き起こす可能性があります。誰かが+副作用を引き起こすために演算子をオーバーロードすることはまずありませんが、言語は保証を与えません。(Pythonにも同じことが当てはまりますが、この特定の例には当てはまりません。)

一方、純粋に関数型の言語では、関数が参照的に透過的であるという言語レベルの保証があります。これには多くの利点があります。純粋な関数は簡単にメモできます。グローバル状態に依存せずに簡単にテストできます。また、関数内の値は、並行性の問題を心配することなく、遅延評価または並行して評価できます。Haskellはこれを最大限に活用していますが、他の関数型言語について十分な知識がなく、それらが機能するかどうかを知ることができません。

とはいえ、ほぼすべての言語(Javaを含む)でFPテクニックを使用することは可能です。たとえば、GoogleのMapReduceは機能的なアイデアに触発されていますが、私が知る限り、彼らは大規模なプロジェクトに「機能的な」言語を使用していません(主にC ++、Java、Pythonを使用していると思います)。


2
徹底的な説明のために+1-FP部外者として私でもそれを理解しました。ありがとう!:-)
ペテル・トレック

これまでのところ、この質問に対する最も価値のある答えです。事実と多くの情報に基づいています。よくやった。
ウィルベル

Scalaには末尾再帰があり、Schemeと同様に、再帰呼び出しが末尾位置にある場合は自動的に実行されます(明示的に要求する必要があるClojureとは異なります)。注釈もあるので、コンパイラ末尾再帰コード生成することを確認できます。持っていないのは、Schemeのより一般化されたTCOです。あなたはそれを知っていると思いますが、あなたは他のほとんどの事柄について非常に多くの詳細を調べたので、それは奇妙な解雇/省略に見えました。
-itsbruce

@itsbruceこの投稿はかなり古いもので、IIRC Scalaにはその時点ではありませんでした(または間違っていたかもしれません;)。更新しました。
shosti

私はScalaを最初から使用していませんが、興味を持った2008年に末尾再帰がありました;-)このトピックについて質問する人にこのSO質問を紹介します。その奇妙さに気づいたので、完全性についてコメントした。
itsbruce

11

あなたが言及する言語は非常に異なっています。

PythonとRubyは動的に型付けされた言語ですが、Haskellは静的に型付けされています。Erlangは同時実行言語であり、Actorモデルを使用しますが、他のすべての言語とは非常に異なります。

PythonとRubyには多くの必須の構成要素がありますが、Haskellのようなより純粋な関数型言語では、すべてが何かを返します。つまり、すべてが関数です。


@kRON:まあ、型システムは言語の重要な特性であり、「これらの特定の関数型プログラミング言語が提供するものと何か違うものがあるのか​​?」と尋ねました。確かに、アクターモデルは他の言語でも使用できますが、Erlangには言語に組み込まれています。Erlangは、軽量プロセスを使用し、分散プログラミング用の組み込み言語構造を備えています。つまり、「並行」言語です。
ジョナス

8

いつものようにパーティーに遅れましたが、とにかく物事を言うつもりです。

関数型プログラミング言語は、 関数型プログラミング許可。私たちがその定義に従えば、ほとんどすべての言語は関数型プログラミング言語です。(偶然、OOPにも同じことが当てはまります。必要に応じてCでOOPスタイルで記述できます。したがって、ロジックによれば、CはOOP言語です。)

関数型プログラミング言語を作るのは、プログラミングを許可するものではなく、簡単にプログラミングできるようするものですです。それが鍵です。

そのため、Pythonにはラムダ(非常に貧弱なクロージャのような問題)があり、「map」や「fold」などの機能ライブラリにあるライブラリ関数をいくつか提供します。それは適切な機能に一貫プログラムに不可能に困難であるため、これは、しかし、それを関数型プログラミング言語を作るのに十分ではないスタイルでそれ(と言語確かにこの型を強制しません!)。その中心となるPythonは、状態および状態操作操作に関係する命令型言語であり、これは関数型言語の式および式評価のセマンティクスと単純に対立しています。

では、Python(またはRuby(または選択した言語を挿入))が「関数型プログラミングを行う」ことができるのに、なぜ関数型プログラミング言語があるのでしょうか。Pythonなどは適切な関数型プログラミングを行うことができないためです。それが理由です。


6

Javaで関数型プログラミング行うことができます(http://functionaljava.org/などを参照)。Cでオブジェクト指向プログラミングを行うこともできます。それはただの慣用句ではありません。

したがって、実際には、Erlang、Haskell、Scheme、または特定のプログラミング言語は絶対に必要ではありませんが、それらはすべて異なるアプローチと異なるトレードオフを表し、いくつかのタスクをより簡単にし、より難しくします。使用すべきものは、何を達成したいかによって異なります。


4

この質問は、無数の言語とパラダイムに適用できます。

  • 誰もがC ++を使用しているのに、なぜ他の汎用言語が必要なのですか?
  • Javaは非常に優れたオブジェクト指向言語なので、なぜ他のオブジェクト指向言語が存在するのですか?
  • perlはすばらしいスクリプト言語なので、なぜpythonが必要なのですか?
  • ヤッタ、ヤッタ、ヤッタ

特定の理由ですべてではないにしても、ほとんどの言語が存在します。誰かが現在の言語が満たされない、または不十分に満たされる必要があったからです。(もちろん、これはすべての言語には適用されませんが、私はそれがよく知られている言語のほとんどに適用される感じ。)例えば、Pythonのは、もともとアメーバOSとのインターフェイスに開発された[ 12 ]とErlangのが助けに作成されましたテレフォニーアプリケーションの開発[ 3 ]。したがって、「なぜ別の関数型言語が必要なのか」という質問に対する1つの答えです。[言語を設計する方法を知っている人の名前を挿入]がpythonのやり方を好まなかったため、単純にできます。

それは私が答えだと思うことをかなり要約しています。関数型言語でできることはpythonで何でもできますが、本当にしたいですか?Cでできることはすべて、アセンブリで行うことができますが、したいですか?さまざまな言語は常にさまざまなことを行うのに最適であり、それはそうあるべき方法です。


2

関数型プログラミングは、特定の言語機能と同じくらい設計パラダイムに関するものです。または、別の言い方をすれば、ラムダとマップ関数は関数型プログラミング言語が作成するものではありません。PythonとRubyには、関数型プログラミング言語に触発された機能がいくつかありますが、通常は非常に命令的な方法でコードを記述します。(Cのようなものです。Cでオブジェクト指向のようなコードを書くことができますが、Cをオブジェクト指向言語と真剣に考えている人はいません。)

見てください:関数型プログラミングは、ラムダやmap、または高階関数だけではありません。それはデザインについてです。「真の」関数型プログラミング言語で書かれたプログラムは、関数の構成によって問題を解決します。RubyまたはPythonで書かれたプログラムはFPに似た機能を使用する場合がありますが、通常は合成された関数のセットのようには読みません。


0

あなたが言及した各関数型言語は特定のニッチに非常によく適合し、それぞれが推奨する慣用的なパターンは、ヘルパーモジュールの巨大なライブラリを構築しない限りPythonで達成することが不可能な特定のタスクに非常に適しています。このようなニッチな卓越性の中で最も明白なのは、Erlangの並行性モデルです。他のものにも同様の長所があります。


0

ラムダ計算、LISP、およびSchemeで使用可能なすべての概念はPythonで使用できるため、その中で関数型プログラミングを実行できます。それが便利かどうかは、コンテキストと好みの問題です。

Python(Ruby、Scala)でLISP(または他の関数型言語)インタープリターを簡単に書くことができます(その量は?)。Pythonのインタープリターは、純粋に機能的なものを使用して作成できますが、多くの作業が必要になります。「関数型」言語でさえ、今日ではマルチパラダイムです。

この古くて美しい本には、関数型プログラミングの本質とは何か(ほとんどではありませんが)の答えがあります。


@Arkaaitoしかし、ラムダ計算、LISP、およびSchemeで利用可能なすべての概念がPythonでも利用可能であることに同意しますか?
マークC

すべての LispコンセプトがPythonで利用可能であると確信していますか?マクロ、コードはデータですか?
SKロジック

@ SK-logicマクロはラムダ計算ではありませんが、はい、Pythonで使用できますが、その名前ではありません。Pythonはeval()関数を提供します。これはコードがデータであるために必要なものですが、さらに進んでいます。LISPのように、ランタイム環境のほとんどを変更できます。
アパララ

@Apalala、動的言語eval()はランタイムメタプログラミングです。時には便利ですが、非常に高価です。Lispマクロは異なり、コンパイル時のメタプログラミングです。Pythonでは、使用可能な形式では使用できません。
SKロジック

@ SK-logicこの種のマクロ、プログラムがマクロを変更できないため(またはその存在を知ることさえできないため)、コードがデータであるという前提を破ります。Pythonでは、必要な場合、プログラムは実行時に独自の解析ツリーにアクセスできます。マクロ(静的な前処理)はまったく機能しません。
アパララ

-5

Pythonは機能しないスタイルでプログラミングできるため、FPの純粋主義者には十分ではありません。しかし、より実用的なコーダーは、それについて独断的でなくても、機能的なスタイルの利点を享受できます。

「機能的なプログラマーはむしろ中世の修道士のように聞こえ、人生の喜びを否定し、それが彼を高潔にすることを望んでいます。物質的な利益にもっと関心がある人にとって、これらの「利点」はあまり説得力がありません。機能的なプログラマーは、大きな物質的利益があると主張します…[しかし]これは明らかにばかげています。割り当てステートメントを省略すると、そのような大きな利点がもたらされた場合、FORTRANプログラマーは20年間それを行っていたでしょう。機能がどれほど悪くても、機能を省略することで言語をより強力にすることは論理的に不可能です。」

-ジョンヒューズ、関数型プログラミングが重要な理由


6
同意する。なしの言語gotoはひどいです。
アノン。

2
@Anon:またはその大きな兄弟call-with-current-continuation
クリスジェスターヤング

4
メイソン、あなたが引用している論文は正反対を言っています。あなたの引用は、異なるストーリーを伝える論文のいくつかの段落の断片です。実際、両方の段落を全体として引用した場合、それらは別の意味を明確に示します。
マルコムスタピック

2
@Mason:はい、著者はモジュール化と遅延評価が真の利点であると主張しています。しかし、元の引用がどのように文脈から外れていたかに注意してください。実際には、FPが素晴らしいという結論で論文から段落を引用したときに、著者がFPに対して何かを主張していることを示唆しているようです。少ないほうがいいですね"。論文のタイトルは、著者の意図をかなり明確に示しています。「なぜ関数型プログラミングが重要なのか」;-)純粋なFPラングアンジュが「迷惑だ」という主張を支持するものではありません。
アンドレスF.

6
@Mason Wheeler:ハイブリッド言語は、Pythonに先んじて一気に広がります。たとえば、Lispは1959年まで遡り、実際にはマルチパラダイム言語です。プログラミングに対する機能的アプローチ、手続き型アプローチ、オブジェクト指向アプローチを完全にサポートしています。適切なマクロパッケージを使用すると、ロジックプログラミングも非常に簡単に実行できます。SchemeもPython(および本書)より前のバージョンです。1975年にさかのぼります。たぶん、このプログラミング言語のタイムラインをひと目見てください。
ちょうど私の正しい意見
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.