7

プロジェクトGDB / MIフロントエンド)にFRP(つまり、リアクティブバナナ0.6.0.0)を使用したいと思います。しかし、イベントネットワークを宣言するのに問題があります。

GUIからのコマンドと、GDBからの停止イベントがあります。両方を処理する必要があり、それらの処理はシステムの状態によって異なります。

私の現在のアプローチは次のようになります(これは問題を示すために必要な最小限の複雑さだと思います):

data Command = CommandA | CommandB
data Stopped = ReasonA  | ReasonB
data State = State {stateExec :: Exec, stateFoo :: Int}
data StateExec = Running | Stopped

create_network :: NetworkDescription t (Command -> IO ())
create_network = do
    (eCommand, fCommand) <- newEvent
    (eStopped, fStopped) <- newEvent
    (eStateUpdate, fStateUpdate) <- newEvent

    gdb <- liftIO $ gdb_init fStopped

    let
      eState = accumE initialState eStateUpdate
      bState = stepper initialState eState

    reactimate $ (handleCommand gdb fStateUpdate <$> bState) <@> eCommand
    reactimate $ (handleStopped gdb fStateUpdate <$> bState) <@> eStopped

    return fCommand

handleCommandhandelStopped現在の状態に応じて、コマンドに反応し、イベントを停止します。考えられる反応は、(同期)GDB I / O関数の呼び出しと、状態更新イベントの発生です。例えば:

handleCommand :: GDB -> ((State -> State) -> IO ()) -> State -> Command -> IO ()
handleCommand gdb fStateUpdate state CommandA = case stateExec state of
   Running -> do
     gdb_interrupt gdb
     fStateUpdate f
 where f state' = state' {stateFoo = 23}

問題は、fによって評価されるときにaccumEstate'とは異なる場合があることですstate

時間と同時性の意味論と反応性バナナの「反応」の順序を完全に理解していないので、なぜこれが起こるのか100%確信が持てません。しかし、によって起動された状態更新関数は、状態を変更するhandleStopped前に評価される可能性があると思います。f

fとにかく、 「現在の」状態の仮定が時々間違っているので、このイベントネットワークは一貫性のない状態につながります。

私はこの問題を1週間以上解決しようとしてきましたが、理解できません。どんな助けでも大歓迎です。

4

2 に答える 2

3

いつでも、または発生するeStateUpdateイベントを発生させたいようですか?eStopeCommand

もしそうなら、あなたはそれを2つのイベントの結合として簡単に表現することができます:

let        
    eStateUpdate = union (handleCommand' <$> eCommand)
                         (handleStopped' <$> eStopped)

    handleCommand' :: Command -> (State -> State)
    handleStopped' :: Stopped -> (State -> State)

    eState = accumE initialState eStateUpdate

    etc.

覚えておいてください:イベントは、新しいイベントを作成するために組み合わせることができる通常の値のように動作します。コールバック関数のチェーンを作成しているのではありません。

このnewEvent関数は、外部からイベントをインポートする場合にのみ使用する必要があります。eCommandおよびeStoppedは外部GDBによってトリガーされるため、これが当てはまりますが、イベントeStateUpdateはネットワークの内部にあるようです。


現在のコードの動作に関して、reactive-bananaは、外部イベントを受信すると常に次のことを行います。

  1. すべてのイベントの発生と動作の値を計算/更新します。
  2. reactimatesを順番に実行します。

ただし、ステップ2でネットワークが再度トリガーされる場合があります(たとえば、fStateUpdate関数を介して)。この場合、ネットワークは新しい値を計算し、この関数呼び出しの一部としてreactimatesを再度呼び出します。この後、フロー制御はまだ実行中の最初のシーケンスに戻り、への2番目の呼び出しは奇妙な効果をもたらします。ネットワーク内の動作はすでに更新されていますが、この呼び出しの引数はまだ古い値です。このようなもの:reactimatesfStateUpdate

reactimate1
reactimate2
    fStateUpdate      -- behaviors inside network get new values
        reactimate1'
        reactimate2'
reactimate3           -- may contain old values from first run!

どうやら、これは説明するのが難しいし、推論するのも難しいですが、上記のガイドラインに固執する場合は幸いなことに不要です。


ある意味で、後者の部分は、従来のスタイルでイベントハンドラーを作成する際のトリッキーさを具体化していますが、前者の部分は、FRPスタイルのイベントを使用したプログラミングの(比較的)単純さを具体化しています。

黄金律は次のとおりです。

イベントの処理中に別のイベントハンドラーを呼び出さないでください。

このルールに従う必要はありません。場合によっては便利です。しかし、そうすると事態は複雑になります。

于 2012-08-03T08:38:35.193 に答える
1

私が見る限り、FR​​Pは私の問題の正しい抽象化ではないようです。

そこで、タイプのメッセージを持つアクターに切り替えました State -> IO State

これにより、必要なイベントのシリアル化と、状態の更新時にIOを実行できるようになります。私が失ったのは、イベントネットワークの素晴らしい説明です。しかし、俳優にとっても悪くはありません。

于 2012-08-06T13:00:42.130 に答える