DEV Community

Muhammad Ahmad
Muhammad Ahmad

Posted on

Functional Programming in JS: 0x05 - Implementing Techniques - Theme 2

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('');
Enter fullscreen mode Exit fullscreen mode

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('');
Enter fullscreen mode Exit fullscreen mode

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('');
};
Enter fullscreen mode Exit fullscreen mode

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('');
});
Enter fullscreen mode Exit fullscreen mode

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('');
});
Enter fullscreen mode Exit fullscreen mode

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
});
Enter fullscreen mode Exit fullscreen mode

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.

Functional reactive programming

Let’s build another kind of application that works in much the same way; one that uses functional programming to react to changes in state. But, this time, the application won’t be able to rely on event listeners.

Imagine for a moment that you work for a news media company and your boss tells you to create a web application that tracks government election results on Election Day. Data is continuously flowing in as local precincts turn in their results, so the results to display on the page are very reactive. But we also need to track the results by each region, so there will be multiple objects to track.

Rather than creating a big object-oriented hierarchy to model the interface, we can describe it declaratively as immutable data. We can transform it with chains of pure and semi-pure functions whose only ultimate side effects are updating whatever bits of state absolutely must be held onto (ideally, not many).

And we’ll use the Bacon.js library, which will allow us to quickly develop Functional Reactive Programming (FRP) applications. The application will only be used one day out of the year (Election Day), and our boss thinks it should take a proportional amount of time. With functional programming and a library such as Bacon.js, we’ll get it done in half the time.
But first, we’re going to need some objects to represent the voting regions, such as states, provinces, districts, and so on.

function Region(name, percent, parties) {
    // mutable properties:
    this.name = name;
    this.percent = percent; // % of precincts reported
    this.parties = parties; // political parties
    // return an HTML representation
    this.render = function() {
        var lis = this.parties.map(function(p) {
            return '<li>' + p.name + ': ' + p.votes + '</li>';
        });
        var output = '<h2>' + this.name + '</h2>';
        output += '<ul>' + lis.join('') + '</ul>';
        output += 'Percent reported: ' + this.percent;
        return output;
    }
}

function getRegions(data) {
    return JSON.parse(data).map(function(obj) {
        return new Region(obj.name, obj.percent, obj.parties);
    });
}
var url = 'http://api.server.com/election-data?format=json';
var data = jQuery.ajax(url);
var regions = getRegions(data);
app.container.innerHTML = regions.map(function(r) {
    return r.render();
}).join('');
Enter fullscreen mode Exit fullscreen mode

While the above would be sufficient for just displaying a static list of election results, we need a way to update the regions dynamically. It’s time to cook up some Bacon and FRP.

Reactivity

Bacon has a function, Bacon.fromPoll(), that lets us create an event stream, where the event is just a function that is called on the given interval. And the stream.subscribe() function lets us subscribe a handler function to the stream. Because it’s lazy, the stream will not actually do anything without a subscriber.

var eventStream = Bacon.fromPoll(10000, function() {
    return Bacon.Next;
});
var subscriber = eventStream.subscribe(function() {
    var url = 'http://api.server.com/election-data?format=json';
    var data = jQuery.ajax(url);
    var newRegions = getRegions(data);
    container.innerHTML = newRegions.map(function(r) {
        return r.render();
    }).join('');
});
Enter fullscreen mode Exit fullscreen mode

By essentially putting it in a loop that runs every 10 seconds, we could get the job done.
But this method would hammer-ping the network and is incredibly inefficient. That would
not be very functional. Instead, let’s dig a little deeper into the Bacon.js library.
In Bacon, there are EventStreams and Properties parameters. Properties can be thought of
as “magic” variables that change over time in response to events. They’re not really magic
because they still rely on a stream of events. The Property changes over time in relation to
its EventStream.

The Bacon.js library has another trick up its sleeve. The Bacon.fromPromise() function is a way to emit events into a stream by using promises. And as of jQuery version 1.5.0, jQuery AJAX implements the promises interface. So all we need to do is write an AJAX search function that emits events when the asynchronous call is complete. Every time the promise is resolved, it calls the EvenStream’s subscribers.

var url = 'http://api.server.com/election-data?format=json';
var eventStream = Bacon.fromPromise(jQuery.ajax(url));
var subscriber = eventStream.onValue(function(data) {
            newRegions = getRegions(data);
            container.innerHTML = newRegions.map(function(r) {
                return r.render();
            }).join('');
        }`
Enter fullscreen mode Exit fullscreen mode

A promise can be thought of as an eventual value; with the Bacon.js library, we can lazily wait on the eventual values.

Putting it all together

Now that we have the reactivity covered, we can finally play with some code.
We can modify the subscriber with chains of pure functions to do things such as adding up a total and filtering out unwanted results, and we do it all within onclick() handler functions for buttons that we create.

// create the eventStream out side of the functions
var eventStream = Bacon.onPromise(jQuery.ajax(url));
var subscribe = null;
var url = 'http://api.server.com/election-data?format=json';
// our un-modified subscriber
$('button#showAll').click(function() {
    var subscriber = eventStream.onValue(function(data) {
        var newRegions = getRegions(data).map(function(r) {
            return new Region(r.name, r.percent, r.parties);
        });
        container.innerHTML = newRegions.map(function(r) {
            return r.render();
        }).join('');
    });
});
// a button for showing the total votes
$('button#showTotal').click(function() {
    var subscriber = eventStream.onValue(function(data) {
        var emptyRegion = new Region('empty', 0, [{
            name: 'Republican',
            votes: 0
        }, {
            name: 'Democrat',
            votes: 0
        }]);
        var totalRegions = getRegions(data).reduce(function(r1, r2) {
            newParties = r1.parties.map(function(x, i) {
                return {
                    name: r1.parties[i].name,
                    votes: r1.parties[i].votes + r2.parties[i].votes
                };
            });
            newRegion = new Region('Total', (r1.percent + r2.percent) / 2,
                newParties);
            return newRegion;
        }, emptyRegion);
        container.innerHTML = totalRegions.render();
    });
});
// a button for only displaying regions that are reporting > 50%
$('button#showMostlyReported').click(function() {
    var subscriber = eventStream.onValue(function(data) {
        var newRegions = getRegions(data).map(function(r) {
            if (r.percent > 50) return r;
            else return null;
        }).filter(function(r) {
            return r != null;
        });
        container.innerHTML = newRegions.map(function(r) {
            return r.render();
        }).join('');
    });
});
Enter fullscreen mode Exit fullscreen mode

The beauty of this is that, when users click between the buttons, the event stream doesn’t change but the subscriber does, which makes it all work smoothly.

Summary

JavaScript is a beautiful language

Its inner beauty really shines with functional programming. It’s what empowers its excellent extendibility. Just the fact that it allows first-class functions that can do so many things is what opens the functional flood gates. Concepts build on top of each other, stacking up higher and higher.

In this part, we dove head-first into the functional paradigm in JavaScript.

We covered function factories, currying, function composition and everything required to make it work. We built an extremely modular application that used these concepts. And then we showed how to use some functional libraries that use these same concepts themselves, namely function composition, to manipulate the order of execution.

Throughout the part, we covered several styles of functional programming: data generic programming, mostly-functional programming, and functional reactive programming.

They’re all not that different from each other, they’re just different patterns for applying functional programing in different situations.

In the previous parts, something called Category Theory was briefly mentioned. In the next part, we’re going to learn a lot more about what it is and how to use it.

Discussion (0)