Next: Coroutines, Up: Time [Contents][Index]
(use-modules (sly signal))
Game state is a function of time. The player’s score, the current stage, an enemy’s hit points, etc. all change in response to events that happen at discrete points in time. Typically, this means that a number of callback procedures are registered to respond to events which mutate the relevant data structures. However, this approach, while simple and effective, comes at the price of readability, reproducibility, and expression. Instead of explicitly mutating data and entering “callback hell”, Sly abstracts and formalizes the process using a functional reactive programming style.
In Sly, time-varying values are called “signals”, and they are defined in a declarative and functional manner. Rather than describing the process of mutation procedurally, one describes the relationships between signals instead. The result is a “signal graph”, a directed acyclic graph of event responses.
(define-signal position (signal-fold v+ (vector2 320 240) (signal-map (lambda (v) (v* v 4)) (signal-sample 1 key-arrows))))
This signal describes a relationship between the arrow keys on the
keyboard and the position of the player. signal-sample
is used
to trigger a signal update upon every game tick that provides the
current state of the arrow keys. key-arrows
is a 2D vector
that maps to the current state of the arrow keys, allowing for 8
directional movement. This vector is then scaled 4x to make the
player move faster. Finally, the scaled vector is added to the
previous player position via signal-fold
. The player’s
position is at (320, 240) initially. As you can see, there are no
callbacks and explicit mutation needed, and the position seems to
magically change with the passage of time.
Return #t
if obj is a signal.
Wrap value in a signal.
Create a top-level signal variable called name. If the variable
already exists and refers to a signal then its outputs will be spliced
into the new signal. If the given value is not a signal then it will
be put into one via make-signal
.
define-signal
is particularly useful when working at the REPL.
A top-level signal variable defined by define-signal
can be
redefined at runtime, and the signals that depended on the old signal
will continue to work with the new signal.
Return the value stored within signal.
Return the value stored within object if object is a signal. Otherwise, return object.
Evaluate body in the context of the local bindings defined by
the two-element lists ((var signal) …)
.
signal-let
works like regular let
, except that it
derefences signal before binding to var.
Similar to signal-let
, but the variable bindings are performed
sequentially. This means that all initialization expressions are
allowed to use the variables defined to the their left in the binding
list.
Change the contents of signal to value. This procedure should almost never be used, except to bootstrap a root node of a signal graph.
Create a new signal whose initial value is init and whose future values are calculated by applying proc to the arguments passed when hook is run.
Create a new signal whose value is the that of the most recently updated signal in signal1, signal2, etc. The initial value is that of signal1.
Create a new signal whose value is a list of the values stored in signals.
Create a new signal that applies proc to the values of SIGNAL. More than one input signal may be specified, in which case proc must accept as many arguments as there are input signals.
Create a new signal that takes on the value of value-signal whenever sample-signal receives a new value.
Create a new signal whose value is the negation of signal by
applying not
to each value received.
Create a new signal that applies proc with the value received
from signal and the past value of itself, starting with
init. Like signal-map
, more than one input signal may be
given.
Create a new signal that takes on the value received from signal when it satisfies the procedure predicate. The value of the signal is default in the case that the predicate is never satisfied.
Create a new signal that takes on the value received from signal when it does not satisfy the procedure predicate. The value of the signal is default in the case that the predicate is never satisfied.
Create a new signal that drops the value received from signal
when it is equivalent to the current value. By default, equal?
is used for testing equivalence.
Create a new signal whose value is that of the signal on when the signal predicate is true, or the value of the signal off otherwise.
Create a new signal whose value is always constant no matter the value received from signal.
Create a new signal that increments a counter by step when a value from signal is received, starting from start. By default, start is 0 and step is 1.
Create a new signal that applies proc for side-effects when a value from signal is received. The value of the new signal will always be the value of signal. This signal is a convenient way to sneak in a procedure that with a side-effect into a signal graph. Such a signal might write text to a file, or play a sound.
Create a new signal whose value is a pair, the car of which is the time that the value of signal was received and the cdr of which is the received value.
Create a new signal whose value is the time that the value of signal was received.
Create a new signal that takes on the value of signal every step ticks.
Create a new signal that emits step every step ticks.
Create a new signal that emits the time since signal was updated ever step ticks.
Create a new signal that delays propagation of signal by delay ticks..
Create a new signal that propagates signal at most once every delay ticks.
Create a new signal whose value is the most recently yielded value of
the coroutine defined by body. A special yield
syntax is
available within body to specify which values are passed to the
signal.
Next: Coroutines, Up: Time [Contents][Index]