Ich Bin Mblinner

Unicorns and Strong Typing

There’s a scene in just about any Victorian period movie where a gentleman take off his glove and slaps another gentleman across the face in it. Or, if they’re going for historical accuracy, perhaps just throws it on the ground at his feet. A duel follows shortly after.

The modern equivalent to this ritual is to start a discussion on strong and weak typing. If you’re talking to a Javascript programmer, claim strong typing is good. If you’re talking to a Java programmer, claim weak typing is good. Then chose your weapons.

Where was I? Right, Unicorns. They don’t exist. Neither does strong typing. Or weak typing. More accurately, there does not currently exist a useful definition of the two terms that is widely accepted, and which can be used as the basis for a rational conversation about their relative merits. This is why the terms are only good for starting fights.

Don’t believe me? Let’s take a look at the definitions provided by that ultimate source of truth, Wikipedia.

In computer science and computer programming, a type system is said to feature strong typing when it specifies one or more restrictions on how operations involving values of different data types can be intermixed. The opposite of strong typing is weak typing.

Great, so it’s strongly typed if it “specifies one or more restrictions on how operations involving values of different types can be intermixed”. And its opposite is weak typing. Let’s see what Wikipedia has to say about that.

In computer science, weak typing (a.k.a. loose typing) is a property attributed to the type systems of some programming languages. It is the opposite of strong typing, and consequently the term weak typing has a number of different meanings, just as “strong typing” does.

Well okay then, so we’ve confirmed that weak typing is indeed the opposite of strong typing. That should clear things up. Let’s talk about something else.

Implicit Conversions

Pop quiz, what does the following Ruby and Scala code do that the Javascript code doesn’t?

1
2
3
4
5
6
ruby> foo = 1
 => 1
ruby> bar = 1.0
 => 1.0
ruby> baz = foo + bar
 => 2.0
1
2
3
4
5
6
7
8
scala> val foo = 1
foo: Int = 1

scala> val bar = 1.0
bar: Double = 1.0

scala> val baz = foo + bar
baz: Double = 2.0
1
2
3
4
5
6
javascript> foo = 1
1
javascript> bar = 1.0
1
javascript> baz = foo + bar
2

They all appear to be adding an integer and a decimal number together, but all is not as it seems.

In most languages, an integer and a floating point number are very different things, at least to the compiler. Signed integers are generally represented as two’s complement while decimals are represented with the IEEE’s floating point standard.

This means that to a computer, 1 and 1.0 may not be the same thing. One may be two’s complement, and the other floating point, so 1 and 1.0 may actually be represented by a different sequence of bytes! You can’t directly add them together.

What Ruby and Scala both do is implicitly convert the two’s complement integer into a floating point number before doing the addition. This is most apparently in the Scala case, where Scala tells us that the result of adding together an Int and a Double is, indeed, a Double.

Javascript, on the other hand, doesn’t need to do this conversion because it has only a single numeric type, Number, which is represented as floating point. So in Javascript, 1 and 1.0 are pretty much the same, unlike Ruby and Scala.

Implicit conversions are at the heart of most discussions on strong and weak typing, as is hinted at by Wikipedia definition of strong typing - “…specifies one or more restrictions on how operations involving values of different data types can be intermixed”. The intuition here is that if you can’t reasonably perform an operation on values of two different types, possibly by implicitly converting one type to another, than the language should disallow it.

The problem with that definition is that it fits almost every language, for some definition of reasonable. Without implicit conversions, we’d lose a whole ton of convenience, like adding an integer and double together, or concatenating a number on to a string, or assigning an unboxed int to an Integer in Java. However, the more aggressive a language gets with its implicit conversions, the more dangerous they can get. Java is rather conservative here which is why its often called strongly typed, though the conversion from unboxed to boxed numbers, for instance, can have very surprising effects on a program’s memory usage and runtime.

So what people generally mean when they say weakly typed is ‘holy crap that language does implicit conversions which I think make no bloody sense, and which frighten and concern me’. This, of course, is why Javascript is the example most folks trot out when talking about weakly typed languages. It does some seriously bizarre implicit conversions.

Anyhow, that’s why most discussions about strong and weak typing just degenerate into anarchy. The only workable definition is that a language is weakly typed is that it does implicit conversions you consider bizzare, and it’s strongly typed if it doesn’t. Computer scientists don’t seem to use the terms anymore, they prefer to talk about the type safety of a given type system.

While this is a topic for another day, type safety has to do with the guarantees that a languages typesystem makes. For instance, using the Scala type system guarantees that you’ll spend a non-zero amount of time trying to remember what the compiler can and cannot infer, and using the Javascript typesystem guarantees that you will begin drinking heavily.

As a side note, Scala give programmers the power to define their own implicit conversions. Here, for instance, is an implicit conversion that will convert from a Scala function to a Java Runnable.

1
2
3
implicit def functionToRunnable(fn: () => Unit) = new Runnable() {
  def run() = fn()
}

With that implicit defined somewhere in a project, the Scala compiler will convert Scala functions to Runnables when it has a Function and needs a Runnable. It may seem that exposing this sort of power to application developers is dangerous, but rest assured, it was added to the language only after it was discovered that shipping butcher knives without handles to the entire Scala userbase would be prohibitively expensive.