Can reactive-banana handle cycles in the network?

本小妞迷上赌 提交于 2019-11-29 03:29:10

The question

Does the reactive-banana library support recursively defined events?

has not only one, but three answers. The short answers are: 1. generally no, 2. sometimes yes, 3. with workaround yes.

Here the long answers.

  1. The semantics of reactive-banana do not support defining an Event directly in terms of itself.

    This is a decision that Conal Elliott made in his original FRP semantics and I've decided to stick to it. Its main benefit is that the semantics remain very simple, you can always think in terms of

    type Behavior a = Time -> a
    type Event    a = [(Time,a)]
    

    I have provided a module Reactive.Banana.Model that implements almost precisely this model, you can consult its source code for any questions concerning the semantics of reactive-banana. In particular, you can use it to reason about your example: a calculation with pen & paper or trying it in GHCi (with some mock data) will tell you that the value evtAutoLayout is equal to _|_, i.e. undefined.

    The latter may be surprising, but as you wrote it, the example is indeed undefined: the GUI state only changes if an evtAutoLayout event happens, but it can only happen if you know whether the GUI state changes, which in turn, etc. You always need to break the strangulating feedback loop by inserting a small delay. Unfortunately, reactive-banana doesn't currently offer a way to insert small delays, mainly because I don't know how to describe small delays in terms of the [(Time,a)] model in a way that allows recursion. (But see answer 3.)

  2. It is possible and encouraged to define an Event in terms of a Behavior that refers to the Event again. In other words, recursion is allowed as long as you go through a Behavior.

    A simple example would be

    import Reactive.Banana.Model
    
    filterRising :: (FRP f, Ord a) => Event f a -> Event f a
    filterRising eInput = eOutput
        where
        eOutput  = filterApply (greater <$> behavior) eInput
        behavior = stepper Nothing (Just <$> eOutput)
    
        greater Nothing  _ = True
        greater (Just x) y = x < y
    
    example :: [(Time,Int)]
    example = interpretTime filterRising $ zip [1..] [2,1,5,4,8,9,7]
    -- example = [(1.0, 2),(3.0, 5),(5.0, 8),(6.0, 9)]
    

    Given an event stream, the function filterRising returns only those events that are greater than the previously returned. This is hinted at in the documentation for the stepper function.

    However, this is probably not the kind of recursion you desire.

  3. Still, it is possible to insert small delays in reactive-banana, it's just not part of the core library and hence doesn't come with any guaranteed semantics. Also, you do need some support from your event loop to do that.

    For instance, you can use a wxTimer to schedule an event to happen right after you've handled the current one. The Wave.hs example demonstrates the recursive use of a wxTimer with reactive-banana. I don't quite know what happens when you set the timer interval to 0, though, it might execute too early. You probably have to experiment a bit to find a good solution.

Hope that helps; feel free to ask for clarifications, examples, etc.

Disclosure: I'm the author of the reactive-banana library.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!