Table of Contents
HipHop Module
Modules are the HipHop execution units. Modules can be loaded into a reactive machine or ran by another 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 .
module [ident]( arg, ... ) [implements [mirror] intf, ...] { ... }
Modules parameters are either signals or variables. Variables
are equivalent to variables declared with var
or let
forms. They
denotes Hop values that can be used in any expressions of the module.
They are passed to the module with the run
form.
Modules body 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 mod( in S, out T, inout U, var x ) { ; }
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 Machine
This syntax is a shorthand for declaring a module, creating a machine, and loading that module into the machine.
machine [ident]( arg, ... ) [implements [mirror] intf, ...] { ... }
HipHop Interface
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]( arg, ... ) [extends intf, ...]
Here is an example of interface declaration and use to define a module:
interface common( in S, out T );
module mod( inout U ) implements common { ; }
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 { ; }
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 module( arg, ... )
The module can either be:
ident
, this identifier refers to a module declared under that name. Its is an error to refer to an unbound module.- a dollar expression, which must evaluate to a module. If it fails to do
so, a
TypeError
is signal when the program is construct.
Note: 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, when a dollar expression is used, that expression is evaluated before the HipHop execution.
The arguments can either be:
ident
ident as ident
ident=expression
...
When a module is ran the signal of the callee must be linked to the
caller signals. The purpose of the arguments is to specify are to
proceed to this linking and to pass values to variable arguments. The
first form links a caller signal and a callee signal whose names are
both ident
. The second form links the signals of two different
names. The third assigned the module variable parameter to
expression
. The fourth form links all the caller modules that are
defined at the run
location whose name matches a callee signal.
Top level Definitions
Modules and interfaces have an optional name. When a named is declared at the JavaScript top-level, it is automatically bound to an eponym JavaScript global variable. That is
const intf = hiphop intf( ... );
const mod = hiphop module() implements ${intf} { .... }
are equivalent to:
hiphop interface intf( ... );
hiphop module mod() implements intf { .... }
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.
every1.js
"use hiphop";
"use hopscript";
const hh = require( "hiphop" );
hiphop module prg( in I, O ) {
every( I.now ) {
emit O();
}
}
exports.prg = 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.
value1.js
"use hiphop";
"use hopscript";
const hh = require( "hiphop" );
hiphop module prg( O=5 combine (x, y) => x + y ) {
loop {
emit O( ${5} );
emit O( ${10} );
yield;
}
}
exports.prg = 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
.
run.js
"use hiphop";
"use hopscript";
var hh = require( "hiphop" );
hiphop module sub( S, U, W, Z ) {
fork {
if( S.now ) emit W();
} par {
if( U.now ) emit Z();
}
}
hiphop module main( in S, in U, A, B ) {
run sub( S, U, W as A, Z as B );
}
exports.prg = 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.
interface.js
"use hiphop";
"use hopscript"
const hh = require( "hiphop" );
hiphop interface I1( A, B, C );
hiphop interface I2( D ) extends I1;
hiphop module M2() implements I2 {
emit A( 10 );
emit D( 23 );
}
hiphop module M1( Z ) implements I1 {
run M2( D as Z, ... );
}
const m = new hh.ReactiveMachine( M1 );
m.addEventListener( "A", v => console.log( "got A", v.signalValue ));
m.addEventListener( "Z", v => console.log( "got Z", v.signalValue ) );
m.react();
Example of Mirrored Interfaces
This example defines two modules connected with mirrored interfaces.
imirror.hh.js
"use hiphop";
const hh = require( "hiphop" );
hiphop interface Intf( in I, out O );
hiphop module M1() implements Intf {
if( now( I ) ) emit O();
}
hiphop module M2( out OK ) implements mirror Intf {
emit I();
if( now( O ) ) emit OK();
}
hiphop module Main( OK ) {
signal implements Intf;
fork {
run M1( ... );
} par {
run M2( OK, ... );
}
}
const m = new hh.ReactiveMachine( Main );
m.addEventListener( "OK", v => console.log( "got OK" ) );
m.react();
Generated Modules
This example uses a module generator (the function Timer
). HipHop modules are
lexically scopped with regard to Hop environment so all the expressions they
contain can refer to Hop variables bound in the environment. In this example,
the function parameter timeout
is used in the async
form to set the
timeout duration.
The new modules are directly created when run, using the dollar-form in the main HipHop program.
run3.hh.js
"use hiphop"
"use hopscript"
const gameTimeout = 100;
function Timer( timeout ) {
return hiphop module( tmt ) {
async tmt {
this.timer = setTimeout(() => this.notify( "ok" ), timeout );
} kill {
clearTimeout( this.timer );
}
}
}
hiphop machine mach( O ) {
signal tmt;
fork {
run ${Timer( gameTimeout )}( ... );
} par {
await( tmt.now );
emit O( tmt.nowval );
}
}
mach.addEventListener( "O", evt => console.log( evt.nowval ) );
mach.react();
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.
run4.hh.js
"use hiphop"
"use hopscript"
hiphop module mod( out O, var n ) {
emit O( n );
}
hiphop machine mach( O combine (x, y) => x + y ) {
fork {
run mod( n = 10, ... );
} par {
run mod( n = 100, ... );
}
}
mach.addEventListener( "O", evt => console.log( evt.nowval ) );
mach.react();
Example of Dynamically Generated Interfaces
When interfaces are to be generated dynamically, the XML interface must be used.
imirrordyn.js
"use hiphop";
const hh = require( "hiphop" );
const SIGS = [ {name: "I", direction: "in"}, {name: "O", direction: "out"} ];
const Intf = <hh.interface>
${SIGS.map( sp => <hh.signal name=${sp.name} direction=${sp.dir}/> )}
</hh.interface>
hiphop module M1() implements Intf {
if( I.now ) emit O();
}
hiphop module M2( out OK ) implements mirror Intf {
emit I();
if( O.now ) emit OK();
}
hiphop module Main( OK ) {
signal implements Intf;
fork {
run M1( ... );
} par {
run M2( OK, ... );
}
}
const m = new hh.ReactiveMachine( Main );
m.addEventListener( "OK", v => console.log( "got OK" ) );
m.react();