5

私は最近、関数型プログラミングに取り掛かり、いくつかの種類の副作用を参照透過的な方法で処理するいくつかの方法を学びました。

  • 変数の更新などの変更可能な状態の State モナド
  • コンソールからの読み取り/コンソールへの書き込みなどの I/O 用の IO モナド
  • グラフィックスや入力デバイス イベントなどの双方向性のためのFRP

しかし、今日の「現実世界の」アプリケーションのほとんどは、複数のユーザーが同時に変更できる外部システム (Web サービス、データベースなど) とインターフェースしており、状態や長時間実行される操作などを持っています。したがって、ケースはそれほど単純ではありません。上記のカテゴリのように: システムにエンティティの状態を問い合わせるか、それを制御しようとした結果は、その状態に依存します。さらに、対話性も要件です。ユーザーが任意にクリックできる GUI がいくつかあり、システムからの変更に自動的に対応する必要がある場合もあります。

純粋関数の利点を最大限に活用して、この種のアプリケーションを設計および実装するパターンは何ですか? または、上記のアプローチのいくつかを、私が考えもしなかった方法でこの問題に適用できますか? 言語 (Java や Scala など) は 100% の純度を強制するわけではないので、実際の経験に裏付けられた実用的なソリューションに興味があります。

4

4 に答える 4

3

私は実際に実用的な方法で完成するこの種のことをたくさんやったわけではないので、他の人がもっとできるようになることを願っています. ただし、Scala で Android アプリを作成しましたが、これにはいくつかの要件がありました。UI はインタラクティブで、「状態」はすべて SQLite データベースに保存されます。データベースと UI の両方が Android フレームワークとのインターフェースを必要としますが、Android フレームワークは Java 指向であり、Scala や関数型プログラミングには簡単には適合しません。

私がしたことは、モデル部分が純粋な操作のみをサポートする ADT のセットとして実装される MVC 設計のようなものを採用することでした。これには、モデル コードが Android フレームワークから完全に独立しているという追加の利点があったため、エミュレーターの外で好きな方法でテストできました。

これにより、コントローラー (ビューは非常に薄いレイヤーであり、ほとんどが Android の動作方法で構成されていました) に加えて、「データベースからモデルをロードする」および「モデルをデータベースに保存する」という追加の操作が残りました。Scala であるため、純粋なモデル コードを呼び出して実際のデータ操作を行う非純粋なコードを使用してこれらのパーツを実装しただけです。Haskell では、これらの部分はおそらく完全に IO モナドにあったでしょう[1]。

全体として、これにより、外部システムとのインターフェース時に「穀物に逆らう」必要なしに、問題のドメインを純粋に機能的な観点から考えることができました。データベース レイヤーは、データベース スキーマとデータ モデルに使用した ADT との間のマッピングの問題になります。これらの操作が失敗する可能性があるという事実への対処は、(DB 操作を開始した) コントローラーの責任であり、モデルには影響しません。コントローラの操作は、「このボタンが押されたとき、現在の状態を現在の状態で関数を呼び出した結果に設定し、表示テーブルを更新する」という概念的に非常に単純になります。最終的には、実際のモデル コードよりも、この不純な「接着剤」コードの方がはるかに多くなりましたが、それでも、この方法でプログラムを実行したことは勝利だったと思います。私のアプリの核心は複雑な構造化データの操作であり、それを正しく行うことは最も難しいことでした。残りは、設計が難しいというよりも、書くのが面倒でした。

これは、プログラムにかなりの量の計算がある場合に機能します (必ずしも大量のデータではなく、実際に計算するだけです)。プログラムがさまざまな外部システムをほぼ完全にくっつけている場合、必ずしも得られるものは多くありません。


[1] IO モナドは、コンソールから読み書きするためだけのものではないことに注意してください。プログラムの外部にある何かの状態によって結果が影響を受けるモデリング操作は、まさに IO モナドの目的です。通常、Haskeller は、常に外部システムと対話していない場合 (またはできれば対話している場合でも)、プログラムのほぼ全体で IO モナドの使用を避けようとします。「外部」からのイベントに応答してデータの純粋な計算を実行したり、複雑な場合は、外部イベントに応答して実行する必要がある IO アクションの純粋な計算を実行したりすることもできます。

于 2012-04-11T02:09:06.757 に答える
2

しかし、最近の「実際の」アプリケーションのほとんどは、複数のユーザーが同時に変更できる外部システム(Webサービス、データベースなど)とのインターフェースであり、状態や長時間の操作などがあります。したがって、ケースはそれほど単純ではありません。上記のカテゴリのように:エンティティの状態をシステムに要求するか、エンティティを制御しようとした結果は、その状態によって異なります。さらに、対話性も要件です。ユーザーが任意にクリックできるGUIがいくつかあり、システムからの変更に自動的に対応する必要があるかもしれません。

インタラクティブに、共有状態を同時に編集することは、状態モナドの単なる別の例です。データ構造の編集を構成するためにレンズやその他の抽象化を使用する場合もありますが、内部的には、共有されているグローバル状態だけがあります。

マシンレベルの同時実行サポートが必要な場合は、STM varやMVarなどの同時構造を使用して、同時編集による競合を解決できます。これは、STMまたはIOモナドにいることを意味します。

Haskellパッケージのために、Hackageでのこれらの種類のジョブのために設計されたモナディック環境の例はたくさんあります。

于 2012-04-12T15:37:13.963 に答える
0

非同期ストリーム別名iterateesは、有用で関連性のある抽象化のように思えます。それらをさらに調査する価値があるようです...

于 2012-04-28T16:13:02.720 に答える