19 Feb 2018

Brushing teeth and static checks

tandkram.jpg

The three-year-old at home doesn't like having his teeth brushed. I don't blame him. It's hard at that age to see why someone would get to mess around with a brush in your mouth, when you'd rather be reading a bedtime story, or play with your toys.

But I, a crafty father, have developed a trick. Experienced parents will recognize this as a kind of distraction tactic.

<masak> Noa, I'm going to paint inside your mouth.
<noa> Red, and white, and blue, and pillow.

What he has specified here is a list of colors (different each morning or night), but the last item is not a color. I start brushing, and slowly list the colors he has picked:

* masak brushes noa's teeth
<masak> Red... white... blue...
<masak> ...pillow... HEY, WAIT A MINUTE!
* noa laughs, toothbrush in mouth

Voilà, a boring chore has an element of silly in it, and is much easier for both parent and child.

In programming terms, the item "pillow" doesn't belong in a list of colors. (It doesn't typecheck.) My scripted mock surprise at this makes the three-year-old feel like he has somehow outsmarted the tooth-brushing routine — instead of being an involuntary recipient, he is an active participant (and a mischievous one at that). He's a tooth-brushing renegade.

Some programming languages are permissive like that: they accept any (syntactically correct) program, start running it, and only later exclaim "HEY, WAIT A MINUTE!" when your program runs into some fairly obvious trap or other. The programmer's reaction is usually "oh right, shucks, better fix that", possibly after some thinking and tracing around the code.

Other languages are more strict, and disallow many such mistakes at compile-time (or earlier). The checks are often type checks, but could also be other things, such as reachability checks.

And that's where we are today, in 2018. The IT world is largely split into languages of the former category — like JavaScript, Perl, Python, Ruby, PHP — and languages of the latter category — like Java, C#, C++, Haskell.

There are long-winded discussions and arguments on either side about which type of language is better suited for various tasks. Briefly and a little bit inaccurately, those can be summarized by weighing flexibility/freedom on the one hand against correctness and safety guarantees on the other.

I've been following a bit of a path in the past few years with my JavaScript development:

  • First off, I enjoy the freedom JavaScript offers: yes, it's possible to get late runtime errors, but... coding discipline and enough up-front thought can at least somewhat compensate for that.
  • Increasingly, I've also started to use linters: tools that alert me and my team to unsafe usages of the language. This is especially motivated because JavaScript was developed in a completely different environment decades ago, without today's requirements for scale and complexity. Over time, I came to think of JavaScript as a language that simply comes with a linter; otherwise you're bound to make silly mistakes.
  • Lately, I've taken to TypeScript, which marries the unruly and flexible JavaScript with a static type system. Same old JavaScript, same old flexibility, but with type checks (and control flow checks, etc) at compile-time. With a decent IDE, the errors show up as I write the code. Incredibly, TypeScript keeps innovating with each new release, catching more and more things that can go wrong.

I want to make clear how extremely improbable TypeScript feels as a concept. If someone had asked me, years ago, "can you retrofit a static type system on top of JavaScript while maintaining full backwards compatibility?", I would've have said "no, of course not". But that's what TypeScript does. Part of the secret seems to be that they are using a structural type system, which fits very well with JavaScript's dynamic nature. The type system in TypeScript is probably the least annoying one I've ever used. (And it throws into stark contrast for me how lacking Java, with its rigid nominal type system, is in this regard.)

The other day I came across a blog post called Top 10 JavaScript errors from 1000+ projects (and how to avoid them). The listicle is a good summary of the most common runtime errors in JavaScript code bases. I like it.

But my first thought was Most or all of these problems would be caught by TypeScript. At least if you use TypeScript with enough strictness turned on (such as the options noImplicitAny and strictNullChecks), and provided you don't cheat and manually type variables as any.

My second thought was Why do we do this to ourselves?

I'm belatedly coming to the conclusion that there's no fundamental opposition between the flexibility of scripting languages and the safety guarantees of static languages. We can have the cake and eat it too.  The reason many projects still get those errors in 2018 is... inertia. JavaScript is right there, linters don't catch everything, things that could've been caught go into production.

So, yeah, go check out TypeScript if you haven't already. You might like it. TypeScript's designer Anders Hejlsberg (of C# and Delphi fame) once described TypeScript as a value proposition it's not rational to refuse. It has only benefits, and no drawbacks. That about sums it up for me.

I'm happy with TypeScript, but I want to push the envelope further.  What safety and productivity lurks in other languages? Preferably languages with the ideas of static typing and other safety mechanisms built in from the start. In 2018, I aim to delve into Elm, PureScript, and Reason, to see what I've been missing from the world of static languages. These are environments where some of the silly late errors we sometimes suffer from in JavaScript don't occur at all, because the language is designed so they don't happen. (Hey, if null was a billion-dollar mistake, here's a wild idea but maybe we should try using a language without them?)

It's not "good-bye JavaScript", but it's definitely "hello safe alternatives". It's about time.

Related courses

  • TypeScript

    TypeScript starts from JavaScript and turns it into a safe language. You get gradual type checking, code completion, as well as static checking of the way your code hangs together. JavaScript never had the hard, protective shell that we're used to in contemporary languages. TypeScript acts like a programming exoskeleton, giving you safety, expressive power, and precision on top of the original language. 

    Duration: 2 days
    Price: 21 500 SEK