ReactiveML is an extension of OCaml. Therefore, OCaml phrases are also ReactiveML phrases. For example, we can evaluate an expression (you can simply click on the code to load it in the terminal):
1 + 2 ;;
define a global value (note that the type infered by the compiler is printed first by the ReactiveML compiler and then by the OCaml compiler):
let a = 1 ;;
define a type:
type 'a tree = | Leaf of 'a | Node of 'a * 'a tree * 'a tree ;;
define a recursive function and apply it:
let rec depth t = match t with | Leaf _ -> 1 | Node (_, t1, t2) -> 1 + max (depth t1) (depth t2) ;;
depth (Node (1, Node (2, Leaf 4, Leaf 5), Leaf 3)) ;;
Functions of the OCaml standard library are also available.
List.rev [ "a" ; "b" ; "c" ] ;;
For more details about OCaml programming you can try TryOcaml.
ReactiveML is based on the synchronous model. In this model, time is a succession of instants. Any OCaml function is considered to be instantaneous.
let instantaneous_loop n = for i = 1 to n do print_int i; print_newline () done ;;
instantaneous_loop 10;;
Functions that can be executed through several instants are called
processes. The pause
statement waits for the next instant.
let process non_instantaneous_loop n = for i = 1 to n do print_int i; print_newline (); pause done ;;
To apply a process, we have to use the run
keyword. Such
an expression that takes time has to be executed in the background of the
terminal using the #exec
directive:
#exec (run (non_instantaneous_loop 10));;
Remark: You can use the "suspend" button (or the directive
#suspend;;
) to execute the program step by step. To
return to the sampled mode you can use the "resume" button (or the
directive #resume;;
).
The communication between parallel processes is made through events that are broadcast instantaneously. Hence, an event is present during the instant where it is broadcast, otherwise it is absent.
An event is declared with the signal
construct.
signal s;;
The construct await s
waits the next instant where s is
broadcast.
let process p = await s; print_endline "Hello!" ;;
#run p;;
An event is broadcast using emit
.
emit s;;
Remark: #run p
is a shortcut for #exec (run p)
.
The notion of instant really becomes visible when parallel
computations occur. The parallel execution of two expressions is
denoted e1 || e2
(boolean disjuction is denoted e1
or e2
). It guarantees that the two expressions are executed at
each instant.
#exec ( run (non_instantaneous_loop 10) || run (non_instantaneous_loop 10) );;
Notice that each instance of the
process non_instantaneous_loop
prints one number per
instant. In the following example, the
function instantaneous_loop
is executed in one instant
(since it is not a process, run
is not needed to apply
it)
#exec ( run (non_instantaneous_loop 10) || instantaneous_loop 10 );;
Notice that when instantaneous_loop
starts executing, it
only stops after having printed the ten numbers (i.e., at
termination), and not after each number.
Events can carry values. When a signal is declared, the programmer can specify how to combine multiple values emitted at the same instant (the default behavior is to collect all the values into a list).
Here, we define a signal s
which sums the values it receives
during an instant.
signal s default 0 gather (+);;
To get the value of a signal, we can use the construct await s(x)
in e
which waits for the emission of s
and then executes
e
with x
taking the value carried by the signal
s
.
let process print_s = loop await s(x) in print_int x; print_newline () end ;;
#run print_s;;
emit s 1;;
emit s 2;;
emit s 3; emit s 4;;
It is possible to access the last value of a signal using
the last
operator.
last ?s;;
A process can be stopped when an event is received using
the do/until
construct (note that it is not a loop
construct).
signal kill;;
let process p = do for i = 1 to max_int do print_int i; print_newline (); pause done until kill done ;;
#run p;;
emit kill;;
Processes can also be suspended and resumed using events via the
control/with
construct.
signal ctrl;;
let process p = control for i = 1 to max_int do print_int i; print_newline (); pause done with ctrl done ;;
#run p;;The first emission of the
ctrl
event suspends the execution.
emit ctrl;;The second emission of
ctrl
resumes the execution at the
point where it was suspended.
emit ctrl;;The next emission suspends the execution again.
emit ctrl;;