The language provides a way to manage valued signals. Signals
are built and accessed through the construction emit
and
present
. A value signal is a pair made of (1) a boolean stream
c indicating when the signal is present and (2) a stream sampled on
that clock c 6. In circuit
terminology, we get circuits with enable.
Signals can be built from sampled streams by abstracting their internal clock. Consider again the example given in section 1.2.5. This program can now be accepted if we write:
let node within min max x = o where rec clock c = (min <= x) & (x <= max) and emit o = true when c val within : ’a -> ’a -> ’a => bool sig val within :: ’a -> ’a -> ’a -> ’a sig
It computes a condition c
and a sampled stream
true when c
. The equation emit o = true when c defines
a signal o
which is present and equal to true
when
c
is true. The emit
construction can be considered as a
boxing mechanism which pack a value with its clock condition. The
right part of the construction emit
must be an expression with
some clock type a on c. In that case, it defines a signal
with clock type a sig.
It is possible to test for the presence of a signal expression e by
writting the boolean expression ? e. The following program, for
example, counts the number of instants where x
is emitted.
let node count x = cpt where rec cpt = if ?x then 1 -> pre cpt + 1 else 0 -> pre cpt val count : ’a sig => int val count :: ’a sig -> ’a
The language provides a more general mechanism to test for the presence of several signals and access their values. It is reminiscent of the pattern-matching construct of ML. This pattern matching mechanisn is safe in the sense that it is possible to access the value of a signal only at the instant where it is emitted. This is in contrast with Esterel, for example, where the value of a signal implicitly holds and can be accessed even when it is not emitted.
The following program takes two signals x
and y
and
returns an integer which equals the sum of x
and y
when
both are emitted, it returns the value of x
when x
is
emitted only and the value 0
when only y
is emitted and
0
otherwise.
let node sum x y = o where present x(v) & y(w) -> do o = v + w done | x(v1) -> do o = v1 done | y(v2) -> do o = v2 done | _ -> do o = 0 done end val sum : int sig -> int sig => int val sum :: ’a sig -> ’a sig -> ’a
A present
statement is made of several signal conditions and
handlers. Each condition is tested sequentially. The signal condition
x(v) & y(w)
is verified when both signals x
and y
are present. A condition x(v1)
means “x
is present and
has some value v1
”. The variable v1
can in turn be used
in the corresponding handler. The last signal condition _
stands for the wildcard signal condition which is always verified.
In the signal pattern x(v) & y(w)
, x
and y
are
expressions evaluating to signal values whereas v
and w
stand for patterns. Thus, writting x(42) & y(w)
would mean:
“await for the presence of signal x
evaluating to 42
and the presence of y
.
Note that the output of the function sum
is a regular flow
since the test is exhaustive. Forgetting the default case will raise
an error.
let node sum x y = o where present x(v) & y(w) -> do o = v + w done | x(v1) -> do o = v1 done | y _ -> do o = 0 done end File "test.ls", line 6-10, characters 2-105: >..present > x(v) & y(w) -> do o = v + w done > | x(v1) -> do o = v1 done > | y _ -> do o = 0 done > end The identifier o should be defined in every handler or given a last value.
We can easily eliminate this error by giving a last value to o
(e.g., adding an equation last o = 0
outside of the present
statement). In that case, the default case is implicitly completed
with an equation o = last o
. The other way is to state that
o
is now a signal and is thus only partially defined.
let node sum x y = o where present x(v) & y(w) -> do emit o = v + w done | x(v1) -> do emit o = v1 done | y _ -> do emit o = 0 done end val sum : int sig -> int sig => int sig val sum :: ’a sig -> ’a sig -> ’a sig
A signal pattern may also contain boolean expressions. The following
program, sums the values of the two signals x
and y
provided they arrive at the same time and z >= 0
.
let node sum x y z = o where present x(v) & y(w) & (z >= 0) -> do o = v + w done | _ -> do o = 0 done end
Remark: Using signals, we can mimic the
default
construction of the language
Signal. default x y
emits the value of x
when x
is present and the value of y
when x
is absent and
y
is present.
let node default x y = o where present x(v) -> do emit o = v done | y(v) -> do emit o = v done end
This is, of course, only a simulation since all the information about
clocks has been lost. In particular, the compiler is unable to state
that o
is emitted only when x
or y
are present as
the clock calculus of Signal does for the default operator.
In some circumstances, it can be valuable to prefer sampling operators
when
and merge
in order to benefit from clock
information.