View on GitHub

NoR

There's No Return. A javascript reactive programming engine.

Download this project as a .zip file Download this project as a tar.gz file

NoR - There's No Return.

A reactive programming engine for javascript.

Getting Started

So what's NoR about?, well, simply put it's just my take at what I learned to be reactive programming yet i'm not an expert at all.

Even though the term reactive might seem like unknown, most of us already interact with a reactive system all the time.

When you open your spreadsheet program and start using formulas that use values from other cells, you can observe that if you change a value in one of that cells all other cells that depend on it get updated accordingly, well, spreadsheets are one example of reactive programming.

If you ever played around with protoboards and logic gates, you can recall that you used to connect all those little gates to achieve something, say a counter, a clock or any other digital system, well, that's also reactive, as soon as you connect all the pieces and throw a little voltage at it, you get it to work (or not :D).

In our days, we see reactive systems all around, in recent web development people are using more and more javascript, and as many of us have found, front-end development is almost always enterely event based, that is, when something changes, you have to react. A lot of libraries handle reacting to changes, from using jQuery to act upon a browser event, Backbone.js and Knockout.js to bind your view and model, etc.

There's No Return

Ok, so imagine you build a little logic gate (the NOR gate)

The NOR logic gate

If we try to implement it on javascript, provably it would be like this:

var NOR = function(a, b){
  var q = !(a || b); // just for you to see a, b and q
  return q;
}

so far so good, well, implementing the NOR gate is a trivial program. But what happens with more complex functions that can possibly fail, in you javascript programming, have you ever seen functions like the following?:

var complex = function(some, thing){
   // if everything goes fine, we return a value
   // otherwise the make function will just
   // throw an exception, how else do we handle failure?
   return make(some, thing, strange)
}

the bad thing about functions like this is that its users need to know that it can possibly throw an exception as means of handling failure, (would you really throw an exception just because some value doesnt have the form you expected? please dont!). To fix this, most people give the complex function a callback (or a pair of callbacks) to be called on success and/or error.

var complex = function(some, thing, callback){
   if(everythingGoesFine) {
     callback(successValue, null)
   } else {
     callback(null, errorValue)
   }
}

You see? now There's No Return. All the function does is do its amazing stuff and invoke the callback accordingly. But again as user of this function you need to know that callback can take two arguments and not just only one, or you can make the complex function take two callbacks one for success and one for error. That is a callback for each output the function can have.

How does this all relates to NoR and reactive programming?, read on.

Reacting to change

Suppose we try to implement our NOR logic gate again wihout using return.

var NOR = function(a, b, q) {
  q( !( a || b ) )
}

So now our function takes two values a and b and a callback function q. Every time you invoke this function you give it two values and maybe a different callback. It doenst still reacts to change automatically, whenever the value of a or b change you have to apply them to the function.

The logic gates (or the spreadsheet formulas) react to changes on their input, and automatically produce a new value for them.

Now image our same NOR implementation, but this time with a and b also being functions. These functions if given no arguments just give you the current state of each input, and our function looks like:

var NOR = function(a, b, q) {
  q( !( a() || b() ) )
}

How it works

Using the NoR function to create a gate that reacts to changes on its inputs, and our NOR gate is:

var NOR = NoR(function(a, b, q){
  q( !( a() || b() ) )
})

Looks pretty much like our previous implementation, the only difference being that the function is wrapped with NoR. What NoR does is creating a cell for each function parameter.

A cell is just something that looks like a function and serves to hold a value. When a cell is given no arguments, it just returns its current value, but when an argument is given to it, it sets its new value and notifies everyone interested on that cell of its change.

In our example, a, b and c are cells, but only the first two are used as inputs, the third as an output.

The return value of the NoR function also looks like a function:

; setting a = false b = true
NOR(false, true) ; => undefined

However it doesnt act as one of those functions that return values or accept callbacks. What we have just did is simply setting the a cell to true and the b cell to false. The result of the operation is asynchronous, that is, we cant be sure in what exact second we have the response.

Just like with promises we have something that represents a value in the future, we dont know exactly when, but it will hold the response value.

So what we do for using the value of c is to listen for changes on c by subscribing to it.

For example:

NOR.c.subscribe(function(newC){
  console.log("OK, the new value of c is", newC)
})

NOR.b(false) // here we are changing the value of the b cell

// sometime in the future we see
// a message printed to stdout.

The NoR function form is:

NoR(gate, wiring, self)

Where gate is a function that receives cells and is invoked whenever one of them changes. An wiring function can be specified that receives the same cells than gate for the sake of setting up cell subscriptions or creating inner gates. If a value for self if given it will be wrapped on an special cell named self and its value will be used as this whenever gate gets called, of course changing the gate's self will trigger its evaluation.

For example, to build an XOR gate using just the universal NOR gate, you can do:

Implementing a XNOR gate using NOR gates

// The universal NOR logic gate.
var NOR = NoR(function(a, b, g){
  g( !( a() || b() ) )
})

// the gate has no implementation as it's actually
// the result of the combination of several NOR gates
// and their outputs. We wire all of them on
// the setup function.
//
// Note that the XNOR gate exposes just three ports:
// a, b and q.
//
// However the gates wired inside use some others like
// x, m and n. Follow the image above.
var XNOR = NoR(function(a, b, q){}, function(a, b, x, m, n, q){
  // Using the new operator on a gate
  // simply cretes a copy of that gate
  // and we assign each its ports.
  new NOR(a, b, x)
  new NOR(a, x, m)
  new NOR(b, x, n)
  new NOR(m, n, q)
})

On the server

Install the module with: npm install NoR

var NoR = require('NoR');
NoR.awesome(); // "awesome"

In the browser

Download the production version or the development version.

In your web page:

<script src="dist/NoR.min.js"></script>

Documentation

Use our wiki.

Examples

See the examples/ directory and try running some of them.

$ node examples/ten_times.js

An example on how to implement a JK flip-flop can be found here

JK flip flip

Contributing

All contributions are welcome, of course you fork the proyect do some changes on what you are interested, new features, typos, tests, examples or anything you want and finally you can send a pull request and I'll be more than grateful to you.

Developing

First get a fork of NoR and install its development dependencies.

$ npm install

To run specs use the npm test command.

License

Copyright (c) 2012 Victor Borja Licensed under the MIT license.