2. evaltime and runtime
Execution in alv
is split into two phases - evaltime and runtime.
At evaltime, that is whenever there is change to the source code, alv
behaves similar to a Lisp. But once one such eval cycle has executed,
runtime starts, and alv
behaves like a dataflow system like
PureData, Max/MSP or vvvv.
Every alive expression returns a stream of values. Whenever an input to an
operator changes, the operator (may) update and respond with a change to its
output as well. To see this in action, we need to start with a changing value.
Number literals like 1
and 2
, are evaltime constant, which means simply
that they will never update. Since all inputs to our math/+
operator are
evaltime constant, the result is constant as well. To get some runtime
activity, we have to introduce a side-effect input from somewhere outside the
system.
The time
module contains a number of operators whose outputs update
over time. Lets take a look at time/tick
:
(import* time)
(trace (tick 1))
This will print a series of numbers, incrementing by 1 every second. The
parameter to time/tick
controls how quickly it counts - try changing it to
0.5
or 2
. As you can see, we can change time/tick
while it is
running, but it doesn't lose track of where it was!
All of the other things we learned above apply to streams of values as well -
we can use def
to store them in the scope, transform them using the ops
from the math
module and so on:
(import* time math)
(def tik (tick 0.25))
(trace (/ tik 4))
Note that if you leave the time/tick
's tag in place when you move it into
the def
expression, it will keep on running steadily even then.