Table of Contents

Control Flow Operators

Sequence, Test, Yield, and Parallelism

sequence

Sequences are implicit in HipHop. That is, two statements separated by a ; are executed sequentially.

pragma

Formal syntax

The pragma form executes a JavaScript statement during the reaction.

Example: seq.hh.js

import * as hh from "@hop/hiphop";

hiphop module t() {
   in n;
   pragma { mach.outbuf +=  "1 " + n.nowval + "\n"; }
   pragma { mach.outbuf += "2\n"; }
}

export const mach = new hh.ReactiveMachine(t);
mach.outbuf = "";
mach.react({n: 34});

The JavaScript body may have signal dependencies.

Example: atom-dep-par.hh.js

import * as hh from "@hop/hiphop";

hiphop module prg() {
   inout A combine (x, y) => x + y;
   
   fork {
      loop {
         emit A(0);
         yield;
      }
   } par {
      loop {
         emit A(1);
         pragma { mach.outbuf += A.nowval + "\n"; }
         yield;
      }
   } par {
      loop {
         emit A(2);
         pragma { mach.outbuf += A.nowval + "\n"; }
         yield;
      }
   }
}

export const mach = new hh.ReactiveMachine(prg, "error2");

mach.outbuf = "";
mach.debug_emitted_func = val => mach.outbuf += "[ '" + val + "' ]\n";

mach.react()
mach.react()
mach.react()
mach.react()
mach.react()

 

Under no circumstances JavaScript side effects must be observable from within a HipHop reaction. It is the responsability of the JavaScript program not to execute visible side effects. If such a side effect happen and is observed from within a HipHop reaction, its behavior becomes unpredictable.

if (delay) { block } [else { block }]

Execute the then block is delay is true, execute the optional else block otherwise. The delay expression can be is a JavaScript that evaluates to a boolean. If that expression uses signal attributes (now, pre, nowval, or preval), HipHop computes a flow dependency in order to evalute the delay only when all the values of these attributes are known for the instance. For instance, the test:

if (!SIG.now) { ... }

depends on the now attribute of the signal SIG. It cannot be evaluated before it is know that SIG is now emitted during the reaction. The HipHop runtime system computes these dependencies automatically.

A particular value of a signal can also be checked. For instance:

if(SIG.nowval > 10 && SIG.nowval < 100) { host { console.log("signal in range") } }

See staging for using dynamic signal names in delay expressions.

yield

Formal syntax

A thread of execution can suspend itself for the current instance using the yield construct. The execution will resume after the yield when the react method of the machine will be called again.

Example: weak.hh.js

import * as hh from "@hop/hiphop";

const prg = hiphop module() {
   in S; out O, F, W;
   weakabort (S.now) {
      loop {
         emit O();
         yield;
         emit W();
      }
   }
   emit F();
}

export const mach = new hh.ReactiveMachine(prg, "wabort");

Example: trap-par-3.hh.js

import * as hh from "@hop/hiphop";

const prg = hiphop module() {
   inout S1_and_S2, S1_and_not_S2, not_S1_and_S2, not_S1_and_not_S2;
   signal S1, S2;

   loop {
      T1: fork {
         emit S1();
         yield;
         break T1;
      } par {
         T2: fork {
            emit S2();
            yield;
            break T2;
         } par {
            if( S1.now ) {
               if( S2.now ) {
                  emit S1_and_S2();
               } else {
                  emit S1_and_not_S2();
               }
            } else {
               if( S2.now ) {
                  emit not_S1_and_S2();
               } else {
                  emit not_S1_and_not_S2();
               }
               yield;
            }
         }
      }
   }
}

export const mach = new hh.ReactiveMachine(prg, "trappar3");

fork { ... } par { ... }

Formal syntax

Run all the bodies in parallel. Complete when all bodies have completed.

Example: parallel-unary.hh.js

import * as hh from "@hop/hiphop";

hiphop module prg() {
   out O;
   loop {
      signal L;

      fork {
         emit L();
      } par {
         fork {
            if( L.now ) emit O();
         }
      }

      yield;
   }
}

export const mach = new hh.ReactiveMachine( prg, "parallelunary" );

This example uses two nested fork constructs. The second is synchronized with the first as it waits for an event the first branch is to emit.

halt

Terminate the current thread.

Example: run-add-par.hh.js

import * as hh from "@hop/hiphop";
import { format } from "util";

hiphop module m1() {
   inout S, U, W, Z;
   fork {
      if (S.now) emit W();
   } par {
      if (U.now) emit Z();
   }
}

hiphop module run2() {
   inout S, U, A, B;
   fork "par" {
      run m1() { S, U, A as W, B as Z };
   } par {
      halt;
   }
}

export const mach = new hh.ReactiveMachine(run2, { name: "run2", sweep: false });
mach.outbuf = "";
mach.debug_emitted_func = val => {
   mach.outbuf += format(val) + "\n";
}

mach.outbuf += ("m.inputAndReact(S)") + "\n";
mach.inputAndReact("S")

mach.getElementById("par").appendChild(hiphop run m1() { S, U, A as Z });

mach.outbuf += ("==================== ADD RUN PARALLEL ==================") + "\n";

mach.outbuf += ("m.inputAndReact(U)") + "\n";
mach.inputAndReact("U")

Loop

Loops are so central HipHop program control flow that HipHop proposes several loop constructs, see derived forms. These are all based on a combination of the elementary loop construct and lexical espaces.

loop { ... }

Formal syntax

This is the basis loop construct that implements an infinite loop

Example: sync1.hh.js

import * as hh from "@hop/hiphop";
import { format } from "util";

hiphop module prg() {
   out O;
   signal L;

   fork {
      loop {
         emit L();
         yield;
      }
   } par {
      loop {
         await(L.now);
         emit O();
      }
   }
}

export const mach = new hh.ReactiveMachine(prg, "sync1");
mach.outbuf = "";
mach.debug_emitted_func = emitted => {
   mach.outbuf += format(emitted) + "\n";
};

mach.react()
mach.react()
mach.react()
mach.react()

A loop can be interrupted by exiting with a break statement.

Example: trap-loop.hh.js

import * as hh from "@hop/hiphop";

hiphop module prg() {
   out tick;
   signal cnt = 5;
   
   exit: loop {
      emit tick("loop" + cnt.preval);
      emit cnt(cnt.preval - 1);
      
      yield;

      if (cnt.preval === 0) {
         break exit;
      }
   }
   emit tick("done");
}
   
export const mach = new hh.ReactiveMachine(prg, "TRAP-LOOP");

 

It is not permitted to implement instantaneous loops, that is a loop for which two iterations may execute during the same reaction. This will be rejected by the compiler. All loops must have a yield statement in their control flow.

Suspension

suspend delay { block }

Suspend the execution of block while delay is true.

Example: suspend.hh.js

import * as hh from "@hop/hiphop";

const prg = hiphop module() {
   in I; out J, O;
   suspend (I.now) {
      loop {
         emit O();
         yield;
      }
   }
   emit J();
}   

export const mach = new hh.ReactiveMachine(prg, "SUSPEND");

Example: trap-suspend.hh.js

import * as hh from "@hop/hiphop";

export const mach = new hh.ReactiveMachine(
   hiphop module() {
      in L;
      T1: fork {
         break T1;
      } par {
         suspend (L.now) {
            yield;
         }
      }
      pragma { mach.outbuf += "exit trap\n"; }
   } );

mach.outbuf = "";
mach.react();

Lexical Escapes

HipHop supports an escape mechanism by the means for the exit/break constructs. They enable a program to abort an ongoing computation.

lbl

Formal syntax

An exit is syntactically similar to a JavaScript label. it must be followed by a HipHop statement. This statement can be interrupted by breaking to that exit form.

break lbl

Formal syntax

Abort the execution of the current statement and continue the execution after the statement that follows that break label. The break form can be used to abord an execution thread when used to escape a fork/par form.

Example: timeout.hh.js

import { ReactiveMachine } from "@hop/hiphop";
import * as hh from "@hop/hiphop";

hiphop module prg(resolve) {
   inout X = 1, Y, Z;
   T: {
      signal __internal = -1 combine (x, y) => x + y;

      loop {
         if (__internal.preval === -1) {
            emit __internal(X.nowval + 5);
         }
         if (__internal.nowval === 0) {
            break T;
         }
         async () {
            setTimeout(this.notify.bind(this), 10);
         }
         emit Y();
         emit __internal(__internal.preval - 1);
      }
   }
   emit Z();
   pragma { resolve(false); }
}

export const mach = new hh.ReactiveMachine(prg);
mach.outbuf = "";
mach.batchPromise = new Promise((res, rej) => mach.init(res));
mach.debug_emitted_func = val => val;

mach.addEventListener("Y", function(evt) {
   mach.outbuf += "Y emitted\n";
});

mach.addEventListener("Z", function(evt) {
   mach.outbuf += "Z emitted\n";
});

mach.react();

This example shows how to exit a loop.

Example: trap-par.hh.js

"use @hop/hiphop";
"use hopscript";

import * as hh from "@hop/hiphop";

hiphop module prg() {
   inout A, B, C;
   T: fork {
      emit A();
      break T;
   } par {
      emit B();
      yield;
      emit C();
   }
}

export const mach = new hh.ReactiveMachine( prg, "trappar" );

This example shows how to exit a fork/par.

Example: trap-await-parallel.hh.js

import * as hh from "@hop/hiphop";

hiphop module prg() {
   inout A, B;
   EXIT: fork {
      await(A.now);
      pragma { mach.outbuf += "A\n"; }
      break EXIT;
   } par {
      await(B.now);
      pragma { mach.outbuf += "B\n"; }
      break EXIT;
   }

   pragma { mach.outbuf += "end\n"; }
}

export const mach = new hh.ReactiveMachine(prg);
mach.outbuf = "";

mach.react();
mach.inputAndReact("B");

This example shows that several threads can decide to exit from a fork/par.

Example: p18.hh.js

import * as hh from "@hop/hiphop";

function sum(arg1, arg2) {
   return arg1 + arg2;
}

hiphop module prg() {
   inout S1_and_S2, S1_and_not_S2, not_S1_and_S2, not_S1_and_not_S2;
   loop {
      T1: {
         signal S1;

         fork {
            yield;
            emit S1();
            break T1;
         } par {
            loop {
               T2: {
                  signal S2;
                  
                  fork {
                     yield;
                     emit S2();
                     break T2;
                  } par {
                     loop {
                        if(S1.now) {
                           if(S2.now) {
                              emit S1_and_S2();
                           } else {
                              emit S1_and_not_S2();
                           }
                        } else if(S2.now) {
                           emit not_S1_and_S2();
                        } else {
                           emit not_S1_and_not_S2();
                        }
                        yield;
                     }
                  }
               }
            }
         }
      }
   }
}

export const mach = new hh.ReactiveMachine(prg, "P18");

Derived Forms

every delay { ... }

Formal syntax

A loop executed each time the delay is true. Abort the execution of the body when delay is true. Delays are documented here.

Example: every1.hh.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");

All the delay forms can be used with every. Example: every-delay.hh.js

import * as hh from "@hop/hiphop";

hiphop module prg() {
   in I; out O;
   every count(2, I.now) {
      emit O();
   }
}

export const mach = new hh.ReactiveMachine(prg, "everydelay");

The form every is a derived form. The form:

every (expr) {
   ... body ...
}

is equivalent to:

await (expr);
loop {
  continue: fork {
    ... body ...
  } par {
    await (expr);
        break continue;
  }
}

do { ... } every delay

Formal syntax

Execute the do's body and loop when test is true.

Example: loopeach.hh.js

import * as hh from "@hop/hiphop";

hiphop module prg() {
   in I; out O;
   do {
      emit O();
   } every(I.now)
}

export const mach = new hh.ReactiveMachine(prg, "loopeach");

The form do/every is a dericed form. The form:

do { 
 ...
} every (expr);

is equivalent to:

loop {
  ... body ...
  await (expr);
}

abort delay { ... }

Formal syntax

Execute the abort's body and abort the execution when delay is true.

Example: abort-par.hh.js

"use @hop/hiphop";
"use hopscript";

import * as hh from "@hop/hiphop";

const prg = hiphop module() {
   in I; out O;
   signal L;
   
   fork {
      abort (L.now) {
         loop {
            emit O();
            yield;
         }
      }
   } par {
      await (I.now);
      emit L();
   }
}

export const mach = new hh.ReactiveMachine(prg, "abortpar");

Example: abortpre.hh.js

"use @hop/hiphop";
"use hopscript";

import * as hh from "@hop/hiphop";

hiphop module prg() {
   inout O; inout S;
   loop {
      abort( S.pre ) {
         emit S();
         yield;
         emit O();
      }
      yield;
   }
}

//console.error(prg.pretty_print())

export const mach = new hh.ReactiveMachine(prg, "abortpre");

The form:

abort (expr) { ... body ... }

is equivalent to

exit: fork {
  ... body ... 
} par {
  await (expr);
  break exit;
}

weakabort delay { ... }

Formal syntax

Execute the weakabort's body and abort the execution when delay is true at the end of the reaction

Example: loopeach-weakabort-emit.hh.js

"use @hop/hiphop";
"use hopscript";

import * as hh from "@hop/hiphop";

export const mach = new hh.ReactiveMachine(
   hiphop module() {
      inout A; inout B;
      do {
         weakabort( B.now ) {
            fork {
               yield;
               emit B();
            }
         }
         pragma { mach.outbuf += ( "weakabort terminated 1." ) + "\n"; }
      } every( A.now )
   } );

mach.outbuf = "";
mach.react();
mach.react();
mach.react();
//mach.outbuf += ( machine.pretty_print() ) + "\n";

const machine2 = new hh.ReactiveMachine(
   hiphop module() {
      inout A; inout B;
      do {
         weakabort( B.now ) {
            fork {
               yield;
               emit B();
            }
         }
         pragma { mach.outbuf += ( "weakabort terminated 2." ) + "\n"; }
      } every( A.now )
   } );

machine2.react();
machine2.react();
machine2.react();
machine2.react();
//mach.outbuf += ( machine2.pretty_print() ) + "\n";

const machine3 = new hh.ReactiveMachine(
   hiphop module() {
      inout A; inout B;
      do {
         T: fork {
            await( B.now );
            break T;
         } par {
            yield;
            emit B();
         }
         pragma { mach.outbuf += ( "weakabort terminated 3." ) + "\n"; }
      } every( A.now )
   } );

machine3.react();
machine3.react();
machine3.react();
machine3.react();

Example: weak2.hh.js

"use @hop/hiphop";
"use hopscript";

import * as hh from "@hop/hiphop";

hiphop module m() {
   in S; out O, F, W, Z;
   weakabort( S.now ) {
      loop {
         emit O();
         yield;
         emit W();
         yield;
         emit Z();
      }
   }
   emit F();
}
   
export const mach = new hh.ReactiveMachine( m, "wabort2" );

Example: weak-immediate.hh.js

import * as hh from "@hop/hiphop";

hiphop module m() {
   in S; out O, F, W;
   weakabort immediate(S.now) {
      loop {
         emit O();
         yield;
         emit W();
      }
   }
   emit F();
}

export const mach = new hh.ReactiveMachine(m, "wabortimmediate")

The form:

abort (expr) { ... body ... }

is equivalent to

exit: fork {
  ... body ... 
} par {
  await (expr);
  break exit;
}

[main page] | [documentation] | [language] | [license]