haskellfunctional-programmingreactive-programmingfrpyampa

Confused about diagrams of Yampa switches


There is some diagrams of Yampa switches at:

http://www.haskell.org/haskellwiki/Yampa/switch

http://www.haskell.org/haskellwiki/Yampa/rSwitch

http://www.haskell.org/haskellwiki/Yampa/kSwitch

(and so on).

I've found that the switch, the only one diagram with description, is the easiest one to get understand. The others seems hard to follow the similar symbols to read the diagrams. For example, to try to read the rSwitch with the symbols used in the switch may be:

Be a recursive SF which is always fed a signal of type 'in' and returns a signal of type 'out'. Start with an initial SF of the same type but someone outside the switch function (the ?[cond]) square) may also pass a new SF via an Event (the type Event (SF in out) at the signature) while the condition is satisfied (for the '?' before the [cond] square). In case of the Event, Yampa would use the new SF instead of the existing one. This process is recursive since '?' (can't get it from the diagram except the signature of the rSwitch seems recursive).

And after I look into the source of rSwitch, it looks like it use switch to switch to the same init SF recursively while the t is fired (according to what described in the diagram, although I don't see what the special t would be fired in the source code).

In the Yampa Arcade it explains the dpSwitch with the code and example. And the paper about the game 'Frag' also uses dpSwitch. However the rSwitch seems absent in these tutorial. So I really don't know how to use r- or the k-serial switches, and in what cases we would need them.


Solution

  • All of the switch functions are ways to change a signal function to behave like another signal function. I personally find the Yampa diagrams to be somewhat difficult to parse, but the type signatures of the various switches give good indication for how to understand them. Once you understand the type signatures, the diagrams become much clearer. switch itself is the most basic:

    switch :: SF a (b, Event c) -> (c -> SF a b) -> SF a b
    

    If we look at the type, it tells us exactly what it does: It takes a SF sf and a SF generator sfg. sf produces values of type (b, Event c), and the input to the signal function generator happens to also be of type c. So, whenever sf's event occurs, the SF will switch to the result of the SF generator. Until an event occurs, the resulting SF will return the value of the original SF.

    This idea is also used in the rswitch and kswitch variants but a bit differently.


    rswitch : This is known as an "extrinsic switch", meaning that the SF will switch without any analysis of its input or output. Let's look at the type signature:

    rswitch :: SF a b -> SF (a, Event (SF a b)) b
    

    It takes a single SF that takes as input values of type a and outputs values of type b. rswitch creates a new SF that also produces output b, but takes an additional input of type Event (SF a b). Note that the Event value's type matches the input type. This means that whenever the event happens, this SF will switch to that event value. However, the type of the SF remains SF (a, Event (SF a b)) b. This means that the SF is free to receive additional events with new SFs, which will influence the behavior of the overall SF. One use for this could be AI behaviors in a game:

    moveFollowTarget :: SF TargetPosition Velocity
    moveShootTarget :: SF TargetPosition Velocity
    moveIdle :: SF TargetPosition Velocity
    
    aiMovement :: SF (TargetPosition, Event (SF TargetPosition Velocity)) Velocity
    aiMovement = rswitch moveIdle  -- Initially idle...
    
    aiMovementManager :: SF a (Event (SF TargetPosition Velocity))
    aiMovementManager = ... whatever ...
    

    Here, the aiMovementManager will fire an event whenever the movement behavior of the AI needs to change, and the value of the event will be the SF that the movement should change to.


    kswitch: This is known as an intrinsic switch, since the contents of the SF are analyzed to figure out what the proper switch should be. Let's go over the type signature

    kswitch :: SF a b -> SF (a, b) (Event c) -> (SF a b -> c -> SF a b) -> SF a b
    

    Here, kswitch takes three arguments, sf, analyzer, and mapping. sf is simply a standard SF with inputs of type a and outputs of type b. sf is how the signal behaves initially. analyzer is a SF that takes the input and output of sf and may or may not fire some sort of event whose value has type c. If it doesn't fire an event, then nothing happens, and the SF continues to behave like sf. If it does fire an event, then both sf and the event value are passed to mapping which determines the new SF to switch to. kswitch is useful when changing the way systems behave based on their outputs.

    One example where this is useful is the algorithm for TCP Congestion Avoidance. Here, we look at whether or not we lose network packets, and either increase or decrease the speed at which we request data.