Friday 9 August 2013

The one about static typing

Static Typing

This phrase has been at the centre of a religious war for at least the last 7 years, and there's little sign of an end to the fighting. The recent Javascript Renaissance is what made it relevant to the average Joe programmer.
There was a time in the not-too-distant past, when things like Node.js would have simply been considered dumb, and ignored in favour of a Spring MVC stack or something similar.

Lets talk about compilation (in the commonly talked about sense).
If you've ever learnt C, C++, Java, C# or any other language with a compiler, you're probably familiar with the concept. A compiler takes the source code that you write, and translates it into a form usable by the target runtime, be that machine code for an x86 processor, or Java bytecode for a JVM.
Compilation is the thing you have to do before you can hit 'Run' and see "Hello, World!".
It's the thing that says "Syntax Error" when you've left out a semicolon.

Besides translating code into simpler code(code with less concepts), compilers also implement static type checking; the thing which tells you that you can't call a function expecting an number with a string.

In the days of old, when computers were slow and abstractions were young, this kind of compiler was a necessary part of making your computer program work. Letting your compiler in on the kind of data you're going to be moving around at every stage was needed, so that the produced machine code(written in terms of memory rather than variables) could allocate the correct amounts of memory to store and move data around.
Given this, you couldn't really avoid making sure the right data types where going to the right places, making this basic form of static type checking an easy win.

Simula 67 was, according to Wikipedia, the first language to directly implement constructs like classes and interfaces, giving the compiler more work to do, and checking even more at compile time.

Smalltalk implemented some similar features, but avoided putting this extra machinery in the compiler. It also did away with a lot of the other work that compilers did, making runtime use of only references to data, rather than copying around the different-sized memory blocks for ints, longs, bools, etc...
In fact, Smalltalk didn't really have a compiler in the normal sense, where you apply it to all of your code, then run the result; rather, you compile code construct-by-construct, and add it to the whole, much like Javascript.

More recently, languages like Haskell, Scala, and F#, push the boundaries of how much you can check at compile time, by adding richer constructs to be analysed by the compiler. With this, the compiler is not just a tool to produce machine code; its used also as a platform for analysis.

Static v Dynamic

What we see here are two contrasting ways to write programs, and there are a number of popular languages associated with each side.
In the static camp, we have Java, C and C++ supporting a static type system, and Haskell, Scala, F# and C# (since 4.0) supporting some more advanced features such as variance annotations.

There are also the dynamic ones: Javascript, Python, Ruby and Clojure.

One could write certain forms of type-checking into Javascript or any dynamic language if so inclined, though code making use of it would need to be written differently.
Clojure includes a type system as a library that you can use if you want: https://github.com/clojure/core.typed

On the other side, the aforementioned statically-inclined languages, besides Haskell, allow one to use typecasting to pass around any kind of value with no type-checking whatsoever.

It's just a Feature

Many discussions take place on and off the web as to how crucial these things are to the development of maintainable, well-performing computer programs (arguably the holy grail of software engineering, and the point of everything).
Some systems go even further than type checking, such as Contracts in Eiffel, which seek to prove certain facts not just about which classes are used, but about the possible values which variables/parameters can take, all by providing more annotations to the compiler.

Static typing is lauded by some as being an absolute necessity for making good software, while others talk about static typing as if a premature optimisation, more irrelevant to the problem domain than a helpful tool, making code less amenable to refactoring and evolution.

While once a technical necessity, static type annotations are no longer this, and after much evolution and research by some very clever people is now simply part of static type checking: a very advanced, powerful feature in some cases, but no longer a necessity for the reasons it once was.

At the dynamic end of the spectrum, Clojure at its most basic supports minimal checking of the existence of variables, in that you can't write a function which makes use of a variable or function you haven't already declared one way or another; that's all.
Is it as powerful as Haskell's typeclasses? Of course not.
Is it less cool? Arguably.
Is it any less useful? Depends.

To determine if a feature is useful, you need to consider who's going to be using it, what it's going to be used for, and under what conditions.
I'm going to shy away from permuting all the combinations of those that I can think of, and instead ask you to consider the Bash shell:
It's arguably one of the more dynamic languages you can get. You can write literally anything in Bash, and there's no compiler to complain. Everything's a string and parsed into other forms when necessary. If you're using a Linux system(even Android), the chances are that a large part of the system is written in Bash. 
With the complete lack of any compile-time checking or compile-time anything-whatsoever, it still somehow works.

No comments:

Post a Comment