Primes Tabserver example

In this version of the Primes example, we use a Tabserver service to decide whether a number is prime.

Figure: Primes Tabserver example - Overview


When SPARKL receives a candidate number, it starts a transaction to work out if the number is prime or not.

A transaction can start either because:

  • You manually enter an integer for the field n
  • The Backend service, which was specified as a client for the CheckPrime solicit, is started and solicits an n of 13
    Note: If you start a transaction without starting the Backend service first, you end up with two transactions:
    • One started by you
    • One started by Backend, a client on CheckPrime
Tip: Get the code from the SPARKL public repository.
Table 1. Primes Tabserver - Markup
Example Description
<folder name="Primes">
  <field name="NO"/>
  <field name="YES"/>
  <field name="MAYBE"/>
  <field name="n" 
    type="integer"/>
  <field name="div" 
    type="integer"/>
  <service name="Connection"
    provision="tabserver_connection"/>
  <service name="Sequencer" 
    provision="sequencer"/>
  <service name="Backend" 
    provision="tabserver"
    dependencies="Connection">
    <prop name="tabserver.browser.src" 
      src="Backend?prop=tabserver.browser.src" 
      type="application/javascript"><![CDATA[
      ...JavaScript comes here...
    ]]></prop>
  </service>
  <mix name="Mix">
    <folder name="Frontend">
    ...      
    </folder>
    <folder name="Backend">
    ...  
    </folder>
  </mix>
</folder>
In this mix we use:
  • Five fields sent between the operations
  • The Connection service, which accepts connections from browsers. It is a dependency for the Backend service.
  • The Backend service, which implements multiple operations in this mix. This service is provisioned on a tabserver
  • Two folders containing five operations
The Backend service uses a piece of JavaScript to implement the operations. See Figure 2 on the script.
<folder name="Frontend">
  <solicit name="CheckPrime" 
    service="Sequencer" 
    fields="n">
    <response name="Yes" 
      fields="YES"/>
    <response name="No" 
      fields="NO"/>
  </solicit>
  <consume name="Log"
     service="Backend"
     fields="n YES"/>
</folder>
The CheckPrime solicit sends the field n, and starts a transaction.

The transaction ends if Sequencer manages to collect either:

  • n and YES - Prime response is sent

  • n and NO - NotPrime response is sent

If a field set contains n and YES, Backend implements the Log operation.

<folder name="Backend">
  <request name="FirstDivisor" 
    service="Backend" 
    fields="n">
    <reply name="Ok" 
      fields="div"/>
  </request>
  <request name="Test" 
    service="Backend" 
    fields="div n">
    <reply name="Yes" 
      fields="YES"/>
    <reply name="No" 
      fields="NO"/>
    <reply name="Maybe" 
      fields="MAYBE"/>
  </request>
  <consume name="Iterate" 
    service="Backend" 
    fields="MAYBE div n">
    <reply name="Ok" 
      fields="div n"/>
  </consume>
</folder>
The FirstDivisor operation adds the field div to the field set, with the value 2.

The field set now contains n and div.

The Backend service implements Test to make some calculations with n and div.

Based on the calculations, Test sends either the Yes, No or Maybe reply.

The field set now contains either of the following:

  • n, div and YES - n is prime
  • n, div and NO - n is not prime
  • n, div and MAYBE - at this point it cannot be decided if n is prime

If Test sends the Maybe reply, Backend implements the Iterate operation.

Iterate is a consume/reply. The consume forms a goal as reply-less consumes usually do. A reply to a consume creates a new field set, and thus, a parallel thread of execution.

A parallel thread does not count as a new transaction. Only one response will be sent to the CheckPrime solicit. Consumes, however, like Iterate itself, can be satisfied multiple times in the same transaction by parallel threads.

When implementing Iterate, Backend uses its JavaScript to create a new field set, where:
  • n retains its value
  • div gets a new value based on the JavaScript
    Note: The value of the new div is 3 on the first iteration. It is incremented by two on every next iteration. For example, 3,5,7...
Iterate sends this new field set to Test.

Test again sends either the Yes, No or Maybe reply.

If the Maybe reply is sent, Iterate creates yet another field set, and so on, until Test sends either Yes or No.

Previous field sets, which contain n, div and MAYBE, are discarded after satisfying the Iterate consume, as they cannot satisfy other goals.

Figure: Primes - JavaScript content of Backend

(function(sparkl) {

  console.warn("Hello from the primes backend");

  sparkl.service({

    /**
     * Properties accessed thru 'this'.
     */
    FIRST_DIVISOR : 2,

    onInit : function() {
      var solicit = this.solicit("CheckPrime");
      solicit
        .value("n", 13);
      var v = solicit.values();
      console.warn(["Starting...", v]);
      solicit
        .send()
        .then(
          function(response) {
            var n = response.value("n");
            if (response.subject.name() === "Prime") {
              console.log("Prime: " + n);
            }
            else {
              console.log("Not prime: " + n);
            }
          },

          function(error) {
            console.error(error);
          });
    },

    /**
     * Called back on connection close, we close the window too.
     */
    onClose : function(closeEvent) {
      console.warn("Closed primes backend");
      if (typeof window !== "undefined") {
        //window.close();
      }
    },

    "Log" : function(oneWay) {
      console.warn("Logged prime: " + oneWay.value("n"));
    },

    /**
     * This operation returns the first divisor.
     * @param request the input function.
     * @param reply the output function.
     */
    "FirstDivisor" : function(request, reply) {
      var n = request.value("n");
      console.warn(["First divisor", n]);
      reply["Ok"]
        .values({
          "div" : this.FIRST_DIVISOR
        }).send();
    },

    /**
     * This operation tests the divisor against n.
     * @param request the input function.
     * @param reply the output function.
     */
    "Test" : function(request, reply) {
      var
        n = Number(request.value("n")),
        div = Number(request.value("div"));

      console.warn(["Testing", n, div]);

      if (div * div > n) {
        reply["Yes"]
          .send();
      }
      else if (n % div === 0) {
        reply["No"]
          .send();
      }
      else {
        reply["Maybe"]
          .send();
      }
    },

    /**
     * This operation generates the next divisor to test.
     * @param request the input function.
     * @param reply the output function.
     */
    "Iterate" : function(request, reply) {
      var serviceImpl = this;
      var n = Number(request.value("n"));
      var div = Number(request.value("div"));
      var nextDiv = (div === serviceImpl.FIRST_DIVISOR) ? 3 : div + 2;

      console.warn(["Iterate", n, div, nextDiv]);

      reply["Ok"]
        .value("n", n)
        .value("div", nextDiv)
        .send()
        .then(
          function() {
            console.log("Trying divisor: " + div);
          },

          function(error) {
            console.error(error);
          });
    }
  });

})(sparkl);