Lesson 1: Quick tour


OCaml code

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.

Logical time

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;;).

Communication

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).

Parallel composition

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.

Valued events

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;;

Preemption

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;;

Suspend/resume

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;;