Table of Contents
HipHop Modules
Modules are the HipHop architectural tool for structuring programs. They are syntactic constructs that facilitate code re-use. There is two ways to use a module:
Modules are lexically scoped and each module introduces it own signal and trap scopes. That is:
- a module can only use the signal it defines, either in its argument list or locally;
- trap labels are only visible inside a module.
This chapter documents static modules. Dynamic or staged modules, that is, modules generated dynamically by prior JavaScrpt executions are described in the staging chapter.
module [ident](arg, ...) [implements [mirror] intf, ...] { sigdecl... }
If the optional ident
is specified a JavaScript constant named
ident
is automatically declared and bound to the HipHop module.
That is, the two following forms are equivalent:
const M0 = hiphop module () { ... };
hiphop module M0() { ... };
Modules parameters (arg
, ...) are equivalent to variables declared
with var
or let
forms. They denote Hop values that can be used in
any expressions of the module. They are passed to the module with the
run
form.
Module bodies constist of HipHop statements. The arguments, which are signal declarations, constitute its interface. Module arguments are either input signals, output signals, or input/output signals. Here is an example of a module with an input, an output signal, and an input/output signal:
module M1(x) {
in S; out T; inout U;
...
}
These signals, in addition to the ones declared in the optional
interfaces (intf
, ...), form the module interface. For a
module to be executed, its signals have to be bound the
caller signals in its scope (the scope of the corresponding
run
form).
Within a module, the in
, out
, and inout
signals can be used
as any local signals.
For instance, the M1
module can be used in the following
run
statement from a M2
module.
module M2() {
...
run M1(10) { myS as S, myT as T, U as U }
...
}
In that example, the M2
's myS
signal is bound to
the M1
's S
, myT
to T
and M2
's signal U
is
bound to M1
's signal U
.
★ Example: run2.hh.js
import * as hh from "@hop/hiphop";
hiphop module m1() {
inout T, W, V, Z;
fork {
if (T.now) {
signal L;
emit L();
emit V();
}
} par {
if (W.now) emit Z();
}
}
hiphop module m2() {
in S; in U; inout A, B;
signal L;
emit L();
run m1() { S as T, U as W, A from V, B as Z };
run m1() { S as T, U as W, A from V, B as Z };
}
export const mach = new hh.ReactiveMachine(m2, "run22");
See run
for a complete description
of the run
form.
Signals that are declared after the first body statements are
local signals and they cannot be bound in a run
statement.
module.precompile()
Compiles a module. Compiling a module is optional and serves only the purpose of running preliminary checks such as signal names resolution.
HipHop Interfaces
Module arguments can either be specified in the module declaration or packed into an interface that a module can then implements. The interface declaration is all similar to a module declaration but an interface contains no body.
interface [ident] [extends intf, ...] { ... }
If the optional ident
is specified a JavaScript constant named
ident
is automatically declared and bound to the HipHop interface.
That is, the two following forms are equivalent:
const I0 = hiphop interface { in I, out O };
hiphop interface I0 { in I, out O };
Here is an example of interface declaration and use to define a module:
interface common { in S; out T };
module mod() implements common { inout U; ... }
The signals S
, T
, and U
are all defined in mod
.
A module can also implements the mirror of an interface. This is lets two modules, one emitted, the other receiving to be easily connected. The syntax is as follows:
interface intf(out T);
module producer() implements intf { ; }
module consumer() implements mirror intf { ; }
★ Example: imirror.hh.js
import * as hh from "@hop/hiphop";
hiphop interface Intf { in I; out O };
hiphop module M1() implements Intf {
if (I.now) emit O();
}
hiphop module M2() implements mirror Intf {
out OK;
emit I();
if (O.now) emit OK();
}
hiphop module Main() {
inout OK;
signal implements Intf;
fork {
run M1() { * };
} par {
run M2() { OK, + };
}
}
export const mach = new hh.ReactiveMachine(Main);
mach.addEventListener("OK", v => mach.outbuf += "got OK\n");
mach.react();
Running Modules
A module can be executed either because it is directly loaded into a
reactive machine or because it is ran by other module
via the run
syntactif form.
run ident(arg, ...) { sig, ...}
The module ident
expression must be a JavaScript variable
holding a module.
The module resolution always take place before the HipHop execution. That is, when a module identifier is used, that identifier is searched before the execution.
The arguments of the run
form are bound the module variables.
When a module is ran the signals of the callee are linked to the caller signals. The purpose of the signals list is to specify are to proceed to this linking.
The linkings can either be:
ident
;ident to ident
; bind the caller signal to the module input signalident from ident
; bind the caller signal to the module output signalident as ident
; bind the caller signal to the module signal+
; autocomplete the signals list, bind automatically signals with same names, raises an error if some signals are unbound.*
; autocomplete the signals list, bind automatically signals with same names, but contrary to+
, ignore unbound signals.
Examples
A Basic example
A basic module with one input signal I
, and one input/output signal O
.
Each time I
is received, O
is emitted. The module is directly loaded
into a reactive machine. It constitutes the program this machine will
execute.
★ Example: every1.js
import * as hh from "@hop/hiphop";
hiphop module prg() {
in I; out O;
every (I.now) {
emit O();
}
}
export const mach = new hh.ReactiveMachine(prg, "every1");
Example using combined signals
A module with an input/output signal O
with default value 5
and a combine function.
★ Example: value1.hh.js
import * as hh from "@hop/hiphop";
hiphop module prg() {
out O = 5 combine (x, y) => x + y;
loop {
emit O(${5});
emit O(${10});
yield;
}
}
export const mach = new hh.ReactiveMachine(prg, "value1");
Example of submodule
This example defines a main
module that runs a submodule sub
. The
signals S
and U
are connected to the submodule under the same
name. That is when the input signal S
is sent to the machine, both
modules see it simultaneously and when sub
emits the output signal
U
, the program behaves as if the emission was executed by main
.
★ Example: run.hh.js
import * as hh from "@hop/hiphop";
hiphop module sub() {
inout S, U, W, Z;
fork {
if (S.now) emit W();
} par {
if (U.now) emit Z();
}
}
hiphop module main() {
in S; in U; inout A, B;
run sub() { S, U, A as W, B as Z };
}
export const mach = new hh.ReactiveMachine(main, "run2");
Example of Interfaces
This example defines two interfaces that are used in two distinct modules.
The module M1
runs the module M2
by aliasing its signal Z
to the
M2
's signal D
and by linking all the other signals (A
, B
, and C
)
directly.
★ Example: interface.hh.js
import * as hh from "@hop/hiphop";
hiphop interface I1 { inout A, B, C; };
hiphop interface I2 extends I1 { inout D; };
hiphop module M2() implements I2 {
emit A(10);
emit D(23);
}
hiphop module M1() implements I1 {
inout Z;
run M2() { Z as D, * }
}
export const mach = new hh.ReactiveMachine(M1);
mach.outbuf = "";
mach.addEventListener("A", v => mach.outbuf += ("got A " + v.nowval) + "\n");
mach.addEventListener("Z", v => mach.outbuf += ("got Z " + v.nowval) + "\n");
mach.react();
Example of Mirrored Interfaces
This example defines two modules connected with mirrored interfaces.
★ Example: imirror.hh.js
import * as hh from "@hop/hiphop";
hiphop interface Intf { in I; out O };
hiphop module M1() implements Intf {
if (I.now) emit O();
}
hiphop module M2() implements mirror Intf {
out OK;
emit I();
if (O.now) emit OK();
}
hiphop module Main() {
inout OK;
signal implements Intf;
fork {
run M1() { * };
} par {
run M2() { OK, + };
}
}
export const mach = new hh.ReactiveMachine(Main);
mach.addEventListener("OK", v => mach.outbuf += "got OK\n");
mach.react();
Modules Variables
This example shows how to use module variable parameters. In that example,
two instances of the smae module mod
are executed in parallel construct,
each invoking the module with a different variable paramter value. This
example shows, that each instance "sees" its own copy of the parameter.
★ Example: run4.hh.js
import * as hh from "@hop/hiphop";
hiphop module mod(n) {
out O;
emit O(n);
}
hiphop module prg() {
out O combine (x, y) => x + y;
fork {
run mod(10) { * };
} par {
run mod(100) { * };
}
}
export const mach = new hh.ReactiveMachine(prg);
mach.outbuf = "";
mach.addEventListener("O", evt => mach.outbuf += (evt.nowval) + "\n");
mach.react();