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:

module [ident]( arg, ... ) [implements [mirror] intf, ...] { ... }

Formal syntax

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, ...] { ... }

Formal syntax

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, ...]

Formal syntax

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

Formal syntax.

The module can either be:

  1. ident, this identifier refers to a module declared under that name. Its is an error to refer to an unbound module.
  2. 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:

  1. ident
  2. ident as ident
  3. ident=expression
  4. ...

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