JavaScript’s Gulf of Evaluation and Gulf of Execution

<rant>

It came to me yesterday why JavaScript development is such slow going for me. I think it’s because it feels like an OO language, but it’s really not. Particularly when using TypeScript, there are classes, ‘this’, inheritance, etc. The thing is that all these ‘artifacts’, for lack of a better term have been added to the language and aren’t there natively. That means that often, things that would behave as I expect them in a more ‘native’ OO language like Java (or Actionscript for that matter) don’t behave intuitively in JS. This means more time in the debugger saying things like “why isn’t that working?”.

Don Norman describes this as the Gulf of Evaluation and the Gulf of Execution. His canonical example is the settings for his freezer. Unless you know what the controls are affecting behind the panel, the odds of getting the temperature correct in the setup he describes are essentially random.

Now, consider probably the most canonical of the JS quirks, the behavior of this. It has scope only in the current function. Nest a function within a function and the scope of the parent ‘object’ doesn’t exist in the parent’s function. This is because in reality, there is no parent object, just a hierarchy of functions. But we have closure, so by setting ‘self = this’ in the parent, we get an approximation of the desired behavior. And now with fat arrow notation, this can indeed be nested (but Typescript won’t let you inherit a fat arrow method). But you have to know that, just like the mechanism behind the panel of Don Norman’s freezer.

The most recent thing that I had to deal with was the assembly of a hierarchical data provider object from a set of database calls. The structure is pretty straightforward:

dataProvider = {
    items:{
        type1:{
            item1:{...},
            item2:{...}
        },
        type2{
            item1:{...},
            item2:{...}
        },
        ....
    },
    associations:{[{...},{...},{...},...]}
}

I really thought I should be able declare these items on the fly, something like:

dataProvider = {};
dataProvider['items']['type']['item1'] = {...};
dataProvider['items']['type']['item2'] = {...};
dataProvider['associations'] = [];
dataProvider['associations'].push({...});
etc.

Instead, the ‘item1’ object gets created, but the [‘items’] object does not. I expected construction of the object to chain from tail to head like it does with execution (e.g. foo.bar().baz().etc). Instead I get a null object error, and a “why isn’t that working?” moment.

In the end, I had to write some code that created the original object and then before each item was added, to make sure that the appropriate property existed, otherwise create it and add it. So, instead of an hour or so of casual coding, this simple task mushroomed into an afternoon’s worth of careful development and testing.

Interestingly, I had to pick up PHP after a long absence, and for me at least, it behaves the way I expect OO languages to behave, with very consistent quirks that you have to learn once – I’m looking at you __construct()

So that’s why JS development takes too freakin’ long for my calcified OOP brain. But it’s also about why we should also be very careful when we try to make something look like something it’s not.

</rant>