ReactiveML is based on the synchronous reactive model embedded in an ML language (here a subset of OCaml). It provides synchronous parallel composition and dynamic features like the dynamic creation of processes. In ReactiveML, the reactive model is integrated at the language level (not as a library) which leads to safer and more natural programming.
Let hello.rml be a file containing the following program:
let process main = print_endline "Hello World!"
To produce an executable, first the file must be compiled into pure
OCaml code using the following command (the -s
option allows to define the main process):
rmlc -s main hello.rml
Then, the generated OCaml file (hello.ml) can compiled
and linked with the ReactiveML runtime as follows:
ocamlopt -o hello -I `rmlc -where` unix.cmxa rmllib.cmxa hello.ml
It produces an executable file hello which
displays Hello World!.
You can also automate the compilation of ReactiveML programs
using rmlbuild. To do that, first define a
file hello.rmlsim which containts the main process:
sim: main
Then you can compile the ReactiveML file and the generated OCaml file in one command:
rmlbuild hello.rml.native
This tool is based on ocamlbuild.
Terminal symbols are set in typewriter font. Non-terminal symbols are set in italic font. Square brackets [] denote optional components. Curly brackets {} denote zero, one or several repetitions of the enclosed components. Parentheses () denote grouping and | denotes alternatives.
let process <id> { <pattern> } = <expr> in <expr>
process <expr>
Process definition are introduced by the process keyword.
A process can be named (let process id ... ) or anonymous
(process <expr>).
nothing pause halt run <process>
nothing is equivalent to().
pause suspends the
execution until next instant.
halt suspends the
execution forever.
run executes a process.
<expr> ; <expr>
<expr> || <expr>
let <pattern> = <expr> { and <pattern> = <expr> } in <expr>
<expr> |> <expr>
In ReactiveML, expressions can be composed in
sequence (;) or in
parallel (||).
The let/and/in construct computes
several expressions in parallel and gets their values, then it
computes the right of
the in.
The
expression e1 |> e2
executes e1
and e2 in parallel, but at
each instant e1 is executed
before e1 (this construct
is not supported in all runtime).
loop <expr> end while <expr> do <expr> done for <id> = <expr> ( to | downto ) do <expr> done for <id> = <expr> ( to | downto ) dopar <expr> done
loop is an infinite loop. while and for/do are the classical loops. They execute their body several times in sequence. Contrarily, the for/dopar loop executes its body several times in parallel.
signal <id> { , <id> } in <expr>
signal <id> default <value> gather <function> in <expr>
These constructs declare new signals. When a signal is declared, we can define how to combine the values emitted during an instant with the signal/gather construct. If no combination function is given, the behavior of the signal is to collect all emitted values in a list.
emit <signal> [ <value> ]
Signal emissions are instantaneous broadcasting. Hence, a signal is present or absent during an instant but it cannot have both status. The notation emit <signal> is a shortcut for emit <signal> ().
present <signal> then <expr> else <expr> await [ immediate ] <signal> pre <signal>
The expression present tests the status
of a signal. If the signal is present,
the then branch is executed
instantaneously, otherwise the else
branch is executed at the following instant.
The expression await s
waits s to be emitted and terminates at
the following instant. Whereas the expression
await immediate s waits s to be emitted and
terminates instantaneously.
Like in Esterel, the non-immediate version
of await is the default one such
that await s; await s waits two
occurrences of s, while
await immediate s; await immediate s is
equivalent to await immediate s.
The expression pre s evaluates
to true if the signal
s has been emitted at the preceding
instant. Otherwise, it evaluates to false.
await <signal> (<pattern>) [ when <expr> ] in <expr> await [ immediate ] one <signal> (<variable>) in <expr> pre ?<signal> last ?<signal> default ?<signal>
The await/in waits the
emission of a signal. At the instant following the
emission, the body is executed in an environment where
the pattern is bind to the value of the signal (the
combination of the values emitted at the preceding
instant). Notice that the await/in keeps waiting when
the value of the signal does not match the pattern or
if the condition specified after the when keyword is
not satisfied.
The await/one/in construct waits the emission
of a signal to bind the pattern with one of
the emitted values. In case of multiple
emission during an instant, the choice of the
value is not specified. Like await, the body
of the expression is executed at the instant
following the reception of the signal (except
if there is the immediate keyword). To be
causal by construction, there is no immediate
version of the await/in construct.
The expression pre ?s evaluates to the value
associated to s at the preceding instant. If s
has not been emitted at the preceding instant,
pre ?s is equal to the default value given at
the declaration point of the signal. last ?s
has a slight different behavior. It evaluates
to the last value associated to s when it was
emitted. While s has never been emitted, pre
?s and last ?s both evaluates to the default
value of s.
The default function returns the default value
of a signal.
do <expr> when <signal> done control <expr> with <signal> [ (<pattern>) [ when <expr> ] done do <expr> until <signal> [ (<pattern>) [ when <expr> ] [ -> <expr> ] ] done
do/when and control/with
allows to suspend the execution of and
expression. The do/when executes its body only
when the signal is present. The control/with
switches between an active mode and a
suspended one each time that the signal is
present.
The preemption construct do/until stops the
execution of its body at the end of instant
when the signal is emitted. The second
do/until executes a handler when a preemption
occurs.