関数型言語での非同期プログラミング


31

私はほとんどC / C ++プログラマーです。つまり、私の経験の大部分は手続き型およびオブジェクト指向のパラダイムに関するものです。ただし、多くのC ++プログラマーが認識しているように、C ++は長年にわたって機能的なスタイルに重点を移しており、最終的にC ++ 0xにラムダとクロージャーが追加されました。

とにかく、C ++を使用した関数型スタイルでのコーディングの経験はかなりありますが、LispやHaskellなどの実際の関数型言語の経験はほとんどありません。

私は最近、これらの言語の研究を始めました。なぜなら、特に並行性と分散コンピューティングへの応用に関して、純粋に機能的な言語の「副作用なし」という考えが常に興味をそそっていました。

ただし、C ++のバックグラウンドから来ると、この「副作用なし」の哲学が非同期プログラミングでどのように機能するかについて混乱しています。非同期プログラミングとは、ユーザーが提供するイベントハンドラーをディスパッチして、非同期に発生するイベントを処理するフレームワーク/ API /コーディングスタイルを意味します(プログラムのフロー外)。これには、Boost.ASIOなどの非同期ライブラリ、または単なる古いCさえ含まれます。シグナルハンドラまたはJava GUIイベントハンドラ。

これらすべてに共通することの1つは、非同期イベントハンドラーが呼び出されたことをプログラムのメインフローが認識するために、非同期プログラミングの性質が副作用(状態)の作成を必要とするように見えることです。通常、Boost.ASIOのようなフレームワークでは、イベントハンドラーはオブジェクトの状態を変更するため、イベントの効果はイベントハンドラー関数の有効期間を超えて伝播されます。本当に、イベントハンドラは他に何ができますか?コールポイントがないため、コールポイントに値を「返す」ことはできません。イベントハンドラはプログラムのメインフローの一部ではないため、実際のプログラムに影響を与える唯一の方法は、状態を変更することです(またはlongjmp、別の実行ポイントに変更することです)。

そのため、非同期プログラミングは、副作用を非同期的に生成することがすべてのようです。これは、関数型プログラミングの目標と完全に矛盾しているようです。これら2つのパラダイムは、関数型言語でどのように(実際に)調整されますか?


3
うわー、私はちょうどこのような質問を書いて、それを置く方法を知りませんでしたし、提案でこれを見ました!
アモグタルパリカー

回答:


11

関数型プログラミングの理解が少し極端すぎると思うことを除いて、すべてのロジックは健全です。現実世界の関数型プログラミングでは、オブジェクト指向プログラミングや命令型プログラミングと同様に、考え方や問題への取り組み方が重要です。アプリケーションの状態を変更しながら、関数型プログラミングの精神でプログラムを作成できます。

実際、アプリケーションの状態を変更して、実際に何かをする必要があります。Haskellの連中は、自分のプログラムが「純粋」であることを告げるでしょう。なぜなら、彼らはすべての状態の変化をモナドにラップしているからです。しかし、彼らのプログラムはまだ外の世界と相互作用しています。(それ以外の点は何ですか!)

関数型プログラミングでは、意味がある場合に「副作用なし」を強調しています。ただし、あなたが言ったように、実世界のプログラミングを行うには、世界の状態を変更する必要があります。(たとえば、イベントへの応答、ディスクへの書き込みなど)。

関数型言語での非同期プログラミングの詳細については、F#の非同期ワークフロープログラミングモデルを検討することを強くお勧めします。ライブラリ内のスレッド遷移の厄介な詳細をすべて隠しながら、機能的なプログラムを作成できます。(Haskellスタイルのモナドに非常によく似た方法で。)

スレッドの「本体」が単純に値を計算する場合、複数のスレッドを生成し、それらに並列に値を計算させることは、依然として機能パラダイムの範囲内です。


5
また、Erlangを見ると役立ちます。言語は、(すべてのデータが不変である)非常に、シンプルに純粋であり、かつあるすべての非同期処理について。
9000

基本的に、機能的アプローチの利点を理解し、重要な場合にのみ状態を変更すると、Javaのようなもので作業している場合でも、状態を変更するタイミングと制御を維持する方法がわかります。
アモグタルパリカー

同意しない-プログラムが「純粋な」関数から構成されているという事実は、外の世界と反復しないことを意味するのではなく、1つの引数セットのプログラム内のすべての関数が常に同じ結果を返すことを意味し、これは(純度)大したことです。実用的な観点から、このようなプログラムはバグが少なく、より「テスト可能」であり、関数の正常な実行が数学的に証明できるからです。
ギルベイツ

8

これは興味深い質問です。私の考えでは、Clojureで採用され、このビデオで説明されているアプローチが最も興味深いです。

http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey

基本的に提案される「解決策」は次のとおりです。

  • ほとんどのコードは、不変のデータ構造を持ち、副作用のない古典的な「純粋な」関数として記述します
  • 副作用は、ソフトウェアトランザクションメモリルールの対象となる変更を制御する管理参照を使用して分離されます(つまり、可変状態へのすべての更新は、適切な分離トランザクション内で行われます)
  • この世界観を見ると、非同期状態の「イベント」を、更新自体が純粋な機能である可変状態のトランザクション更新のトリガーとして見ることができます。

私はおそらく他の人ほど明確に考えを表明していませんが、これが一般的な考えを与えることを願っています-基本的には、並行STMシステムを使用して純粋な関数型プログラミングと非同期イベント処理の間の「橋」を提供することです。


6

1つの注意:関数型言語は純粋ですが、ランタイムはそうではありません。

たとえば、Haskellランタイムにはキュー、スレッド多重化、ガベージコレクションなどが含まれますが、これらはすべて純粋ではありません。

良い例は怠です。Haskellは遅延評価をサポートしています(実際にはデフォルトです)。操作を準備して遅延値を作成すると、この値の複数のコピーを作成できますが、必要ない限り「遅延」のままです。結果が必要な場合、またはランタイムがある程度の時間を見つけた場合、値は実際に計算され、遅延オブジェクトの状態は、結果を取得するために計算を実行する必要がなくなったことを反映するように変更されます。これはすべての参照を通じて利用できるようになったため、純粋な言語であるにもかかわらず、オブジェクトの状態が変更されました。


2

この「副作用のない」哲学が非同期プログラミングでどのように機能するかについて私は混乱しています。非同期プログラミングとは...

それがポイントです。

健全で副作用のないスタイルは、状態に依存するフレームワークと互換性がありません。新しいフレームワークを見つけます。

たとえば、PythonのWSGI標準では、副作用のないアプリケーションを構築できます。

考え方は、さまざまな「状態の変化」が、インクリメンタルに構築できる値の環境に反映されるということです。各リクエストは、変換のパイプラインです。


「副作用アプリケーションを構築することはできません」どこかに欠けている言葉があると思います。
クリストファーマハン

1

Cを学習した後、Borland C ++からカプセル化を学習しましたが、Borland C ++にジェネリックを有効にするテンプレートがなかったため、オブジェクト指向のパラダイムに不安を感じました。やや自然な計算方法は、パイプを介したフィルタリングデータと思われます。外側のストリームは、副作用として考えられるのではなく、内側の不変の入力ストリームから独立した独立したアイデンティティを持ちました。つまり、すべてのデータソース(またはフィルター)は他から独立していました。keypress(サンプルイベント)は、非同期ユーザー入力の組み合わせを利用可能なキーコードに制限しました。関数は入力パラメーターの引数で動作し、クラスによってカプセル化された状態は、関数の小さなサブセット間で繰り返し引数を明示的に渡すことを避けるためのショートカットにすぎません。

特定のパラダイムを厳守すると、漏れやすい抽象化に対処するのに不都合が生じます。JRE、DirectX、.netなどの商用ランタイムは、何よりもまずオブジェクト指向の支持者を対象としています。不便さを抑えるために、言語はHaskellのような学術的に洗練されたモナドを選択するか、F#のような柔軟なマルチパラダイムサポートを最終的に取得します。多重継承のユースケースからカプセル化が有用でない限り、マルチパラダイムアプローチは、いくつかの、時には複雑な、パラダイム固有のプログラミングパターンに対する優れた代替手段になる可能性があります。

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