16

起動時に2つのことを実行したいイベントトリガーがあるとします。まず、いくつかの動作の値を更新する必要があります。次に、他の条件が満たされた場合、動作の更新された値で別のイベントsend_offを発生させたいと思います。コード形式で表現され、私が持っていると仮定します

trigger :: Event b
trigger = ...

updateFromTrigger :: b -> (a -> a)
updateFromTrigger = ...

conditionFromTrigger :: b -> Bool
conditionFromTrigger = ...

behavior :: Behavior a
behavior = accumB initial_value (updateFromTrigger <$> trigger)

send_off :: Event a
send_off = ?????? (filterE conditionFromTrigger trigger)

次に、質問は次のとおりです。??????に何を入れますか これにより、send_offは動作の最新の値を送信します。これは、適用されたばかりのトリガーからの更新を含む値を意味します。

残念ながら、私が正しく理解していれば、Behaviorのセマンティクスは、更新された値がすぐに利用できないようなものです。したがって、ここでの唯一のオプションは、基本的に、作業を複製し、Behaviorの更新された値を再計算して、すぐに使用できるようにすることです。別のイベントでは、つまり?????? のようなもので

send_off =
    flip updateFromTrigger
    <$>
    behavior
    <@>
    filterE conditionFromTrigger trigger

これで、ビヘイビアの代わりにディスクリートを使用することで、ビヘイビアの更新された情報をすぐに利用できるようになるという感覚がありますが、これは実際には、元のイベントと同時に発生するイベントを提供することと同じです。更新された値を使用し、リアクティブなものを見逃していない限り、バナナは、他の2つのイベントが同時に発生した場合にのみイベントを発生させる方法を提供しません。つまり、イベントの結合を提供しますが、交差点は提供しません。

だから私は2つの質問があります。まず、この状況についての私の理解は正しいですか、特に上記の私の解決策がそれを回避する唯一の方法であるという結論で正しいですか?第二に、純粋に好奇心から、イベントの交差点に対処する方法について開発者による考えや計画はありましたか?

4

1 に答える 1

6

素晴らしい質問です!

残念ながら、ここには簡単な解決策がない根本的な問題があると思います。問題は次のとおりです。最新の累積値が必要ですが、同時に発生するイベント(まだ順序付けられている)がtrigger含まれている可能性があります。それで、

同時アキュムレータ更新のどれが最新になりますか?

重要なのは、更新はそれらが属するイベントストリームで順序付けられますが、他のイベントストリームとの関係では順序付けられないということです。ここで使用されるFRPセマンティクスは、どの同時更新がどの同時イベントにbehavior対応するかを認識しなくなりました。send_off特に、これは、提案された実装send_offが正しくない可能性があることを示しています。動作が複数回更新される可能性があるため、同時イベントが含まれている場合は機能しませんtriggerが、更新を再計算するのは1回だけです。

これを念頭に置いて、私は問題へのいくつかのアプローチを考えることができます:

  1. mapAccum新しく更新されたアキュムレータ値で各トリガーイベントに注釈を付けるために使用します。

    (trigger', behavior) = mapAccum initial_value $ f <$> trigger
        where
        f x acc = (x, updateFromTrigger acc)
    
    send_off = fmap snd . filterE (conditionFromTrigger . fst) $ trigger'
    

    このソリューションはモジュール性の点で少し欠けていると思いますが、上記の議論に照らして、これはおそらく回避するのが難しいでしょう。

  2. の観点からすべてをリキャストしますDiscrete

    ここでは具体的な提案はありませんが、あなたのsend_offイベントは、適切なイベントではなく、値の更新のように感じられる可能性があります。Discreteその場合、同時イベントが発生したときにそのApplicativeインスタンスが「正しいこと」を行うという観点からすべてをキャストする価値があるかもしれません。

    同様の精神で、私はそれがより自然に感じるので、changes . accumD代わりによく使用します。accumE

  3. リアクティブバナナの次のバージョン(> 0.4.3)には、関数が含まれる可能性があります

    collect :: Event a   -> Event [a]
    spread  :: Event [a] -> Event a
    

    その具象化、それぞれ。同時イベントを反映します。とにかくタイプを最適化するためにそれらが必要Discreteですが、それらはおそらく現在の質問のようなものにも役立ちます。

    特に、イベントの共通部分を次のように定義できます。

    intersect :: Event a -> Event b -> Event (a,b)
    intersect e1 e2
            = spread . fmap f . collect
            $ (Left <$> e1) `union` (Right <$> e2)
        where
        f xs = zipWith (\(Left x) (Right y) -> (x,y)) left right
          where (left, right) = span isLeft xs 
    

    ただし、上記の説明に照らして、この関数は思ったよりも役に立たない場合があります。特に、それはユニークではなく、多くのバリエーションがあります。

于 2011-12-24T14:23:19.280 に答える