17

私はreactive-bananaを使用したプログラムに取り組んでおり、基本的なFRPビルディングブロックを使用してタイプを構造化する方法を考えています。

たとえば、実際のプログラムの簡単な例を次に示します。たとえば、システムが主にウィジェットで構成されているとします。プログラムでは、時間の経過とともに変化するテキストの断片です。

ができた

newtype Widget = Widget { widgetText :: Behavior String }

しかし、私も持つことができます

newtype Widget = Widget { widgetText :: String }

Behavior Widget時変の振る舞いについて話したいときに使用します。これにより、作業が「簡単」になり、Behaviorウィジェットを解凍して再パックするのではなく、操作をより直接的に使用できるようになります。

一方、前者は、ほとんどすべてのウィジェットが時間の経過とともに変化するため、実際にウィジェットを定義するコードの重複を回避しているようです。また、ウィジェットをBehavior組み合わせることで、ウィジェットを定義しないものもいくつか定義していることに気付きます。より一貫した方法で他のもの。

別の例として、両方の表現で、インスタンスを持つことは理にかなっていMonoidます(そして、私はプログラムにインスタンスを持ちたいです)が、後者の実装はより自然に見えます(リストモノイドをニュータイプに簡単に持ち上げるだけなので) )。

(私の実際のプログラムではではDiscreteなくを使用Behaviorしていますが、それは適切ではないと思います。)

同様に、Behavior (Coord,Coord)またはを使用(Behavior Coord, Behavior Coord)して2Dポイントを表す必要がありますか?この場合、前者は当然の選択のようです。しかし、それがゲーム内のエンティティのようなものを表す5要素のレコードである場合、選択はあまり明確ではないように思われます。

本質的に、これらの問題はすべて次のようになります。

FRPを使用する場合、どのレイヤーでBehaviorタイプを適用する必要がありますか?

Event(程度は低いですが、同じ質問が当てはまります。)

4

2 に答える 2

6

FRPアプリケーションを開発するときに使用するルールは次のとおりです。

  1. 「変化するもの」を可能な限り隔離します。
  2. 「同時に変化するもの」を1つにグループ化しBehaviorますEvent

(1)の理由は、使用するデータ型が可能な限りプリミティブである場合、抽象操作の作成と作成が容易になるためです。

これは、説明したように、などのインスタンスMonoidをrawタイプに再利用できるためです。

Lensesを使用すると、データ型の「コンテンツ」を生の値であるかのように簡単に変更できるため、ほとんどの場合、余分な「ラッピング/アンラッピング」は問題になりません。(この特定のレンズ実装の概要については、この最近のチュートリアルを参照してください。他にもあります

(2)の理由は、不要なオーバーヘッドを取り除くだけだからです。2つのものが同時に変化する場合、それらは「同じ動作をする」ので、そのようにモデル化する必要があります。

Ergo / tl; drnewtype Widget = Widget { widgetText :: Behavior String }(1)のために使用する必要がBehavior (Coord, Coord)あり、(2)のために使用する必要があります(通常、両方の座標が同時に変更されるため)。

于 2011-12-21T22:25:10.290 に答える
5

dflemstrのアドバイスに同意します

  1. 「変化するもの」を可能な限り隔離します。
  2. 「同時に変化するもの」を1つにまとめBehavior/Eventます。

そして、これらの経験則の追加の理由を提供したいと思います。

質問は次のように要約されます。時間とともに変化する値のペア(タプル)を表現したい場合、質問は使用するかどうかです。

a。(Behavior x, Behavior y)-一対の行動

b。Behavior (x,y)-ペアの振る舞い

どちらか一方を優先する理由は次のとおりです。

  • a以上b

    プッシュ駆動型の実装では、動作の変更により、それに依存するすべての動作の再計算がトリガーされます。

    xここで、値がペアの最初のコンポーネントのみに依存する動作について考えてみます。バリアントaでは、2番目のコンポーネントを変更してyも動作は再計算されません。ただし、バリアントbでは、その値が2番目のコンポーネントにまったく依存していなくても、動作が再計算されます。言い換えれば、それはきめの細かい依存関係と粗い依存関係の問題です。

    これはアドバイス1の議論です。もちろん、両方の行動が同時に変化する傾向がある場合、これはそれほど重要ではなく、アドバイス2が得られます。

    もちろん、ライブラリは、バリアントbに対してもき​​め細かい依存関係を提供する方法を提供する必要があります。リアクティブバナナバージョン0.4.3の時点では、これは不可能ですが、今のところ心配しないでください。私のプッシュ駆動型の実装は、将来のバージョンで成熟する予定です。

  • b以上

    リアクティブバナナバージョン0.4.3はまだ動的イベント切り替えを提供していないため、すべてのコンポーネントを単一の動作にした場合にのみ作成できる特定のプログラムがあります。正規の例は、可変数のカウンターを特徴とするプログラム、つまりTwoCounter.hsの例の拡張です。あなたはそれを時間とともに変化する値のリストとして表現しなければなりません

    counters :: Behavior [Int]
    

    行動の動的なコレクションを追跡する方法がまだないためです。そうは言っても、リアクティブバナナの次のバージョンには動的イベントスイッチングが含まれます。

    また、いつでも問題なくバリアントaからバリアントbに変換できます。

    uncurry (liftA2 (,)) :: (Behavior a, Behavior b) -> Behavior (a,b)
    
于 2011-12-24T09:42:38.403 に答える