Inline Callbacks: More Optimized, More Refactored

What’s cool here is that, unlike traditional inlining, we aren’t just performing inline expansion of some monomorphic function. Instead, via callbacks, we are able to define entire subprograms and sequences of statements to execute. This grants us amazing expressive power. We are able to write clear, readable code without sacrificing performance. Take this very simple example:

JavaScript++:
[js]
runTwice(function(){ log("Hello World!"); });
[/js]

Java:

[cpp]
for (var i = 0; i < 2; i++)
{
log("Hello World!");
}
[/cpp]

Both snippets of code are attempting to log a message twice. However, the JS++ code is much more readable.

At first glance, it would appear the JS++ code will run slower. After all, it’s dealing with two function calls versus the zero for Java. However, let’s take a closer look at how runTwice is actually defined in JavaScript++:

[java]
inline void runTwice(inline void callback())
{
callback();
callback();
}
[/java]

After the compiler desugars and performs inline expansion, our original code becomes:

[js]
log("Hello World!");
log("Hello World!");
[/js]

What you are seeing is that there is ZERO performance penalty.

The benefits are enormous from readability to refactoring to maintainability. Now that you’ve had a brief introduction of inline callbacks and the vast expressive power they offer you, let’s dive into deeper, more real-world examples.

Enumerating JavaScript Object Properties

Let’s start our real-world examples with yet another intricacy of JavaScript that JS++ fixes. Today, the problem is the JavaScript for-in loop. As you may know, when you use the for-in loop on a JavaScript object, it will also enumerate all the properties it inherited through the prototype chain. This is obviously undesirable as, oftentimes, JavaScript objects are used more like associative arrays.

[js]
Object.prototype.oops = 1;
var position = { x: 1, y: 2 };
for (var prop in position) console.log(prop);

// Output:
// x
// y
// oops
[/js]

More enlightened souls know to use hasOwnProperty:

[js]
Object.prototype.oops = 1;
var position = { x: 1, y: 2 };
for (var prop in position) {
if (position.hasOwnProperty(prop)) {
console.log(prop);
}
}

// Output:
// x
// y
[/js]

The use of “hasOwnProperty” as demonstrated above is the most common case I see. Unwittingly, these programmers didn’t account for the edge case:

[js]({hasOwnProperty: 1}).hasOwnProperty("hasOwnProperty"); // TypeError[/js]

Sure, you can argue this is an edge case, but, assume the object is being used like an associative array. Assume the key is the username. If a user registers with the username, “hasOwnProperty”, your application breaks. Instead, the actual best practice is to use Object.prototype.hasOwnProperty:

[js]Object.prototype.hasOwnProperty.call({hasOwnProperty: 1}, "hasOwnProperty"); // true[/js]

Now your loop looks like this:

[js]
for (var prop in position) {
if (Object.prototype.hasOwnProperty.call(position, prop)) {
console.log(prop);
}
}
[/js]

Even more enlightened souls know they should cache when looping. So, finally, we have this:

[js]
var hasOwn = Object.prototype.hasOwnProperty;
for (var prop in position) {
if (hasOwn.call(position, prop)) {
console.log(prop);
}
}
[/js]

Every single time you want to use an object as an “associative array”, you end up writing that convoluted mess above. What I want to demonstrate with this trivial but common example is that even the most trivial and smallest pieces of code can be refactored without a performance hit. In fact, in most cases, it will be faster than the JavaScript equivalent.

Here are some real-world examples of the pattern in use in one of JavaScript’s most popular libraries, Underscore:

Now that we’ve established the problem, let’s take a look at the JavaScript++.


[java]
class SimpleAssociativeArray
{
private var data = {};

// Enumerate an object’s properties
private inline function loopProps(inline void callback(data, prop))
{
var hasOwn = _Object.prototype.hasOwnProperty;

for (var prop in data)
{
if (hasOwn.call(data, prop))
{
callback(data, prop);
}
}
}

// Get the "keys" of the associative array
public string[] getKeys()
{
loopProps(function(data, prop) {
keyArray.push(data[prop]);
});
}

// Clear the associative array
public void clear()
{
loopProps(function(data, prop) {
delete data[prop];
});
}
}
[/java]

The magic is in the loopProps method. It’s an inline function which also defines an inline callback. Again, at first glance, you see two function calls (loopProps and its callback). When both are inlined, the methods using loopProps will look more like this:

[java]
class SimpleAssociativeArray
{
public string[] getKeys()
{
var hasOwn = _Object.prototype.hasOwnProperty;

for (var prop in data)
{
if (_Object.prototype.hasOwnProperty.call(data, prop))
{
keyArray.push(data[prop]);
}
}
}

public void clear()
{
var hasOwn = _Object.prototype.hasOwnProperty;

for (var prop in data)
{
if (hasOwn.call(data, prop))
{
delete data[prop];
}
}
}
}
[/java]

We’ve just now gone from two function calls to zero. Again: “What I want to demonstrate with this trivial but common example is that even the most trivial and smallest pieces of code can be refactored without a performance hit.” If you were to attempt this granularity of refactoring in regular JavaScript, you’re going to feel the sluggishness gradually creep in until it kills you. If Underscore refactored like this at the library level, it will slow down every application depending on the library.

Although contrived, imagine an example where we wanted to introduce “private” properties which are prefixed by __. Rather than go through every loop that enumerates object properties to skip over private properties, if we wanted to solve a problem using “private” properties, we can just modify the loopProps function. By encouraging refactoring, inline callbacks make your application more maintainable in the long run.

Ultimately, inline callbacks bring us one step closer to declarative programming without any performance costs.