restrict";). Continue reading for an explanation of how it works and why I think it’s useful.
Of course, this is nothing new. The idea of writing programs that for example perform advanced number arithmetics on operands of type strings seems silly. Still, the language lets us do exactly that. I’m arguing that much of the operator semantics complexity in the language, with corresponding type coercion, is not wished for. If we could make some of it go away, fewer bugs would hide and our programs would become more robust. It may also become even more fun to write and debug them.
This was discovered years ago when people started recommending the use of the (strict)
=== operator instead of the (loose)
== operator, and the same for
!=. It’s entirely possible to write programs without using the
== operator at all, and many programmers have chosen to do so. The result? More robust programs that are easier to reason about. For a beginner, it’s also a programming style that’s easier to learn and master.
The problem is that there is no strict version of the (loose)
< operator. Or
- and so on and so forth. Because these are loose and loose-only, we’ve kind of accepted them as-is and moved on. Imagine for a second what would happen if you suddenly had strict versions of these operators available, with the possibility of making the strict versions the default for parts of your program. How would you then define the strict version of
<? I bet you wouldn’t want it to accept an operand of primitive type number and another of primitive type string, since that would most likely be a bug in your program. Would you want it to accept
undefined < 3? I bet not. Perhaps you would like to get notified as early as possible with an exception if your strict rules are broken?
Here’s the thing. We already limit ourselves to a subset of the semantics for these operators. At least we try to. We don’t mix strings, numbers and booleans randomly. We don’t perform arithmetics to
undefined, on purpose. We try to be in control of when type conversion happens, and so on. It’s just that we don’t necessarily use precisely the same subset, because we haven’t really though about what we should be restricting our programs to. And we don’t have any good means of getting notified as soon as we break that subset. We would like to, because no matter how good we are at this language, we still have to fix defects in our code. Defects some of which are related to breaking the subset.
Here are the restrict mode rules:
0. === and !== are already strict by default, with identical semantics in restrict mode 1. == and != are restricted to undefined == null (true) and undefined != null (false). They are otherwise only allowed when the === or !=== operator yields an identical result 2. + < <= >= > are restricted to primitive strings or numbers (but never a mix) 3. - * / % & | ^ ~v << >> >>> -v ++v v++ --v v-- are restricted to primitive numbers 4. That's it! +v !v && || ?: o[k] and all other operators have identical semantics in restrict mode
Many programs already conform to most of these rules, with the notable exception of the restrict
+ operator in clause 2. In restrict mode you can no longer use
str + nostr to perform string conversion and concatenation at the same time. You can easily replace this with a call to a string-formatting function (I use
Fmt) or an explicit conversion via
Clause 1 is a compromise and may need to change. I’d rather see
!= disallowed but there seem to be many programs relying on the
undefined == null behavior. For now I’ve added a pedantic setting where it’s disallowed.
I’d like to hear your thoughts on all this. Does restrict mode resonate with you and if so, do the rules define the right subset?
I have a preliminary web site up at http://restrictmode.org/ with more information. You can try restrict mode live in your browser by following the “Try restrict mode” link. Source code for JSShaper is available at https://github.com/olov/jsshaper.
Follow me on Twitter