Mostly functional programming

What is a program without side effects? A program that does nothing.

Complementing our code with functional code with unavoidable side-effects can be called "mostly functional programming." Using multiple paradigms in the same codebase and applying them where they are most optimal is the best approach. Mostly functional programming is how even the pure, traditional functional programs are modelled: keep most of the logic in pure functions and interface with imperative code.

And this is how we're going to write a little application of our own.

In this example, we have a boss that tells us that we need a web application for our company that tracks the status of the employees' availability. All the employees at this fictional company only have one job: using our website. Staff will sign in when they get to work and sign out when they leave. But that's not enough, it also needs to automatically update the content as it changes, so our boss doesn't have to keep refreshing the pages.

We're going to use Lazy.js as our functional library. And we're also going to be lazy: instead of worrying about handling all the users logging in and out, WebSockets, databases, and more, we'll just pretend there's a generic application object that does this for us and just happens to have the perfect API.

So for now, let's just get the ugly parts out of the way, the parts that interface and create side-effects.

function Receptor(name, available){
  this.name = name;
  this.available = available; // mutable state
  this.render = function(){
    output = '<li>';
    output += this.available ? 
      this.name + ' is available' : 
      this.name + ' is not available';
    output += '</li>';
    return output;
  }
}
var me = new Receptor;
var receptors = app.getReceptors().push(me);
app.container.innerHTML = receptors.map(function(r){
  return r.render();
}).join(''),

This would be sufficient for just displaying a list of availabilities, but we want it to be reactive, which brings us to our first obstacle.

By using the Lazy.js library to store the objects in a sequence, which won't actually compute anything until the toArray() method is called, we can take advantage of its laziness to provide a sort of functional reactive programming.

var lazyReceptors = Lazy(receptors).map(function(r){
  return r.render();
});
app.container.innerHTML = lazyReceptors.toArray().join(''),

Because the Receptor.render() method returns new HTML instead of modifying the current HTML, all we have to do is set the innerHTML parameter to its output.

We'll also have to trust that our generic application for user management will provide callback methods for us to use.

app.onUserLogin = function(){
  this.available = true;
  app.container.innerHTML = lazyReceptors.toArray().join(''),
};
app.onUserLogout = function(){
  this.available = false;
  app.container.innerHTML = lazyReceptors.toArray().join(''),
};

This way, any time a user logs in or out, the lazyReceptors parameter will be computed again and the availability list will be printed with the most recent values.

Handling events

But what if the application doesn't provide callbacks for when the user logs in and out? Callbacks are messy and can quickly turn a program into spaghetti code. Instead, we can determine it ourselves by observing the user directly. If the user has the webpage in focus, then he/she must be active and available. We can use JavaScript's focus and blur events for this.

window.addEventListener('focus', function(event) {
  me.available = true;
  app.setReceptor(me.name, me.available); // just go with it
  container.innerHTML = lazyReceptors.toArray().join(''),
});
window.addEventListener('blur', function(event) {
  me.available = false;
  app.setReceptor(me.name, me.available);
  container.innerHTML = lazyReceptors.toArray().join(''),
});

Wait a second, aren't events reactive too? Can they be lazily computed as well? They can in the Lazy.js library, where there's even a handy method for this.

var focusedReceptors = Lazy.events(window, "focus").each(function(e){
  me.available = true;
  app.setReceptor(me.name, me.available);
  container.innerHTML = lazyReceptors.toArray().join(''),
});
var blurredReceptors = Lazy.events(window, "blur").each(function(e){
  me.available = false;
  app.setReceptor(me.name, me.available);
  container.innerHTML = lazyReceptors.toArray().join(''),
});

Easy as pie.

Note

By using the Lazy.js library to handle events, we can create an infinite sequence of events. Each time the event is fired, the Lazy.each() function is able to iterate one more time.

Our boss likes the application so far, but she points out that if an employee never logs out before leaving for the day without closing the page, then the application says the employee is still available.

To figure out if an employee is active on the website, we can monitor the keyboard and mouse events. Let's say they're considered to be unavailable after 30 minutes of no activity.

var timeout = null;
var inputs = Lazy.events(window, "mousemove").each(function(e){
  me.available = true;
  container.innerHTML = lazyReceptors.toArray().join(''),
  clearTimeout(timeout);
  timeout = setTimeout(function(){
    me.available = false;
    container.innerHTML = lazyReceptors.toArray().join(''),
  }, 1800000); // 30 minutes
});

The Lazy.js library has made it very easy for us to handle events as an infinite stream that we can map over. It makes this possible because it uses function composition to take control of the order of execution.

But there's a little problem with all of this. What if there are no user input events that we can latch onto? What if, instead, there is a property value that changes all the time? In the next section, we'll investigate exactly this issue.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.184.102