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 ;;
Functions that can be executed through several instants are called
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 (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
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
await s waits the next instant where s is
let process p = await s; print_endline "Hello!" ;;
An event is broadcast using
#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
e1 || e2 (boolean disjuction is denoted
or e2). It guarantees that the two expressions are executed at
#exec ( run (non_instantaneous_loop 10) || run (non_instantaneous_loop 10) );;
Notice that each instance of the
non_instantaneous_loop prints one number per
instant. In the following example, the
instantaneous_loop is executed in one instant
(since it is not a process,
run is not needed to apply
#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
in e which waits for the emission of
s and then executes
x taking the value carried by the signal
let process print_s = loop await s(x) in print_int x; print_newline () end ;;
emit s 1;;
emit s 2;;
emit s 3; emit s 4;;
It is possible to access the last value of a signal using
A process can be stopped when an event is received using
do/until construct (note that it is not a loop
let process p = do for i = 1 to max_int do print_int i; print_newline (); pause done until kill done ;;
Processes can also be suspended and resumed using events via the
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
ctrlevent suspends the execution.
emit ctrl;;The second emission of
ctrlresumes the execution at the point where it was suspended.
emit ctrl;;The next emission suspends the execution again.