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.
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.