25 May 2016

Statically Typed Languages Need Better Support for Dynamic Typing

Statically typed languages support dynamic typing in many ways. Let’s take Java as an example.

First is reflection, which lets you invoke any method you want, possibly resulting in an exception if it doesn't exist.

The second example is downcasting a reference and invoking a method on it. This can result in an exception at runtime, rather than a compiler error, which by definition makes it dynamically typed. Consider this Java code:

void doSomething(Collection c) {
  Object first = ((List)c).get(0);
}

This can result in an exception at runtime, which is no different from the following Ruby code:

def doSomething(collection)
  first = collection.at(0);
end

Both functions throw an exception if you invoke them with an argument of the wrong type. So, both these are, in essence, dynamically typed.

The third example of dynamic typing is a fat interface — an interface that declares methods that may or may not be implemented, in which case they throw an exception. Lest you think that this is rare in well-written code, something as common as Java’s List interface suffers from this problem, where all the add and remove methods are optional. And, when you implement such a method:

void put(int index) {
  throw new UnsupportedOperationException();
}

you have the worst of both worlds — the verbosity of statically typed languages, and the unsafety of dynamically-typed languages.

A special case of a fat interface is arrays in Java. If you have an array of a derived class, the compiler will implicitly convert its type to into an array of base class. The following code compiles:

void store(Object[] names) {
  names[0] = "Kartick";
}

This seems logical, since a String is an Object. But when you invoke the method like this:

Object[] files = new File[100];
store(files);

you get an ArrayStoreException. This is, again, dynamically typed.


Summarising so far, even statically typed languages support dynamic typing in many ways: reflection, downcasting and fat interfaces.

Why not make it less cumbersome to use? Have a type named dynamic on which you can call any method, possibly resulting in an exception. Just think of this as a better syntax for reflection. If a language has reflection, it might as well have a sane syntax for it.

In fact, some languages do have such a type: C# has dynamic and Objective C has id. More languages should have such a type.

With a dynamic type, there's less need for reflection, downcasting or fat interfaces.

Or code generation, which doesn't help when you're reading from something like a JSON object, which is dynamically typed to begin with. Having to do object.get("age") is no safer than just object.age. No language can make JSON typesafe, because the JSON object may not have the property you're trying to read, or it may have the wrong type. You must deal with runtime errors. All static typing adds is verbosity and bureaucracy.

All languages should support dynamic typing, by allowing you to declare the type of a variable as dynamic. Not necessarily that everything should be dynamically typed, or that dynamic typing should be the default, but that it should be available when needed. Because there's no way around it.

No comments:

Post a Comment