Wednesday, July 22, 2015

Type Systems

Overview

Static vs. dynamic? Strong vs. weak? What does it all mean? And what's this I keep hearing about ducks? In this post, I will try to shed some light on these mysterious terms...

Static vs. Dynamic Type-Checking

Static type-checking is the process of verifying a program's type safety before it is run (i.e. at compile time). But what does it mean to "verify a program's type safety"? Well, it simply means to insure that types are being used as they are supposed to (int's are used like int's, string's are used like string's, and so on). Therefore, we would expect a statement like int x = "one"; to fail to compile in a statically type-checked language. C, C++, Java, and C# (*see note below) all behave this way.

Dynamic type-checking, on the other hand, is the process of verifying a program's type safety at runtime. Here are a few JavaScript expressions that would fail to execute due to dynamic type-checking:

var n = 123;
console.log(n.toUpperCase()); //TypeError (n is not a String)
n(); //TypeError (n is not a Function)

It is pretty common for interpreted languages (such as JavaScript, Ruby, and Python) to have dynamic type-checking, but some compiled languages have this trait as well. It's also possible for a language to have both static and dynamic type-checking.

*NOTE: C# 4.0 and above allows a variable to be declared as dynamic. This behaves similarly to declaring a variable as an object, except that expressions involving these variables are not type-checked at compile time. This was introduced for interoperability with dynamically type-checked languages.

Strong vs. Weak Typing

The "strong" vs. "weak" type system comparison is not as cut-and-dry or as well-defined as the static vs. dynamic comparison. The difference between strong and weak type systems has to do with how the language will handle variables which do not meet the expected type for the operation. A "strongly" typed language will typically throw an error, while a "weakly" typed language will attempt to convert the argument(s) to complete the operation.

Consider the expression 3 + "4". Attempting this in Ruby will throw a TypeError, whereas JavaScript will coerce the 3 into a string and produce the result "34". Therefore, it can be said that Ruby is strongly typed and JavaScript is weakly typed (note that both languages use dynamic type-checking).

Duck Typing

So what, then, is duck typing? The answer typically given to this problem is the following:

When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.

Yeah, I'm not wild about people who answer straightforward questions with riddles, either.

Consider instead the following JavaScript function and example usage.

var maxLength = function(a, b) {
    return Math.max(a.length, b.length);
};
console.log(maxLength([1, 2, 3], "hello"));

The two arguments could both be strings, could both be arrays, or could be one of each. Or they could be something else altogether. As long as they both have a length property, the function goes quietly about it's business. This is duck typing.

Duck typing is something that can really only manifest in languages with dynamic type-checking, although it is not limited to weakly typed languages. Duck typing differs from languages which use nominal typing, where variables are determined to be type-compatible by comparing specifically declared types. Structural typed languages offer something of a middle-ground between duck typing and nominal typing; two variables are type-compatible if their entire structures are the same (i.e. all of their properties and methods).

Further Reading

If you're looking for more detailed explanations, Wikipedia has a fairly extensive entry on type systems, which also links to entries on the majority of the topics discussed above. I personally find their comparison of type systems found in major languages to be a more useful jumping-off point.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.