Summary
To what extent does the productivity of dynamic languages come from the lack of typed variables versus the reduction in "finger typing" required when you don't have to declare the types? The technique of type inferencing in static languages may shed new light on this old debate.

Advertisement

Over the years here at Artima we've had a number of debates and discussions about static versus dynamic typing. Here are a few:

The accepted wisdom

Throughout all these discussions, I heard some common themes that informed my own opinions on the matter. For example, Java creator James Gosling said this in an interview:

It's funny how some of these things like performance and reliability actually fit hand in hand with developer productivity. There's a folk theorem out there that systems with very loose typing are very easy to build prototypes with. That may be true. But the leap from a prototype built that way to a real industrial strength system is pretty vast.

Strong typing is one reason that languages like C++ and Java require more finger typing. You have to declare all your variables and you have to do a lot of work just to make the compiler happy. An old saying from Unix developers goes something like, "If only your programs would be correct if you simply typed them three times." You'd gladly do that if typing your programs three times was enough to make them work correctly, but unfortunately it doesn't work that way.

...Programs written in dynamic languages are very easy to run and check. So for day-to-day programs that aren't not as serious as enterprise systems, you don't have to be as robust. It's not worth the cost of declaring types, for example. And because there are so many dynamic checks in dynamic languages, in most cases something very terrible usually does not happen. For example, Ruby checks array boundaries, so you don't have buffer overwrites. Ruby doesn't provide direct address manipulations, so you can't crash the stack space. Therefore in Ruby, I want to enable programmers to get a program ready to test in a short time by making programmers productive.

Python tries hard to get out of your way while you're coding, so you can quickly complete the functionality and start testing the program. Java, by contrast, actually tries to slow you down a bit while you're coding—for your own good—so that when you reach the system-testing phase, you already have a more robust system. Python enthusiasts believe they can achieve sufficient robustness through testing, and that they have more time to test because they finish the functionality so much sooner. Java enthusiasts feel that strict compile-time checking will ultimately yield a more robust system, despite taking longer to reach the testing phase.

The rise of "duck" typing

One of the points Guido van Rossum made to me during our interview, and others in subsequent discussions, is that I was not accurate when I described the typing of Python as "weak typing." Guido suggested the term runtime typing:

Weak typing is not really a fair description of what's going on in Python. It's really runtime typing because every object is labeled with a type.

I later heard the term dynamic typing used more frequently than runtime typing. For example, Aahz wrote in his his weblog:

It's very clear that Python has only dynamic typing; any target may hold a binding to any kind of object."

More recently I began to hear this style of typing called "Duck Typing," and this seems to be the most popular term nowadays. As Alex Martelli put it many years back in a post to comp.lang.python:

The basic idea is that in dynamically typed languages, variables can hold references to any type of object. In code you can invoke any method. At runtime, if the method exists on the object, it will be invoked. Otherwise you'll get an exception. Here's an example in the Python interpreter:

>>> v = ["Hi ", "there!"]
>>> v = v[1]
>>> v.upper()
'THERE!'

In the first line of this example, I initialize a variable v to the list ["Hi ", "there!"]. The v variable has no type. It can hold onto any type of object, including a list, which is what it holds initially. In the second line, I extract the first element of the (zero-based) list with v[1], which returns the string "there!". I then place a reference to that string back into the v variable. So although v initially refered to a list, it now refers to a string. In the third line I call the upper method on v. This succeeds because strings in Python do indeed have a method named upper, which returns a new string with the same value as the passed string, except with all lowercase characters converted to uppercase. The interpreter shows that the result is 'THERE!'.

Some of the verbosity comes from the need to indicate the type of the variable, and the verbosity only grows the more you use generics. List<String> may not seem so bad, but things start really feeling verbose when you want to say List<Map<String,List<Boolean>>>. You often need to say it twice, once on the left of the equals sign, and again on the right. For example, in this case, I said List<String> to the left of the equals sign on the first line of the main method, and ArrayList<String> to the right.

The advent of "duh" typing

Lately I have been starting to use Scala more and more for real work. Although it is a statically typed language like Java, Scala uses local type inference to infer types from the context. The upshot is that even though variables have types in Scala, as in Java, I need not specify the types as often in Scala code as I do in Java code. Here's what the example looks like in Scala:

In the first line, I call a factory method named List, passing in two Strings. The Scala compiler knows this factory method will, in this case, return an object whose type is List[String]. (Scala uses square brackets for generics rather than angle brackets as in Java.) Since I didn't explicitly specify in the code a type for v, the Scala compiler infers that v's type is List[String], the type of the object with which it is initialized.

As in the Java example, I am unable to reuse the v variable. (Actually, in the Scala example v is not a variable. It's a value, which is like a final variable in Java.) In both the Scala and Java case, I need to come up with a new variable to hold the String I extract from the List. But whereas I had to explicitly specify that the type of this variable is String in the Java code...

String s = v.get(1);

...the word String does not appear in the corresponding line in the Scala code:

val s = v(1)

The Scala compiler infers the type of s to be String, because that's the type of the expression v(1), with which it is initialized.

About two months ago I gave a talk on little languages and code generation to the SD Forum, and afterwards went out for a beer with a few of the attendees. We ended up talking about type inference in Scala at one point, and the fellow next to me, Randy Kerber, said, "That's duh typing." I said, "No, it's not duck typing." He said, "Not duck typing, duh typing." As in: Knowing that the expression initializing s is a String, the compiler says, "Well, duh! The type of s is String."

Feels like Python

One of the surprises I encountered when starting to use Scala was the realization that I had underestimated the extent to which reduced finger typing contributes to the feeling of productivity enjoyed when programming in dynamic languages. It wasn't as much that variables lacked types in the dynamic languages, but that I didn't have to explicitly "finger type" the types in the code. Scala is statically typed like Java, but it feels a bit like Python to me when I program in it, because it is so much more concise than Java.

Do you think type inference will change the dynamics of the static versus dynamic typing debate? Please post in the forum.

My problem with all these examples is that they are not at all representative of "real" code. For example

>val v = List("Hi ", "there!")

Other than in unit tests, I'm hard-pressed to remember ever wanting to write such code. An object will have a list, and it will be read from a configuration file or have a setter or something, but the times I'm wanted to initialize it with two hard coded strings is, well, never.

In that case, I'm gonna have to write List<String> explicitly, and Java seems much more reasonable typing wise, and the benefits of more explicit type checking are less of a burden to implement.

It might be nice to have a

@duhTyping

annotation - I'd often use it in unit test code, and would avoid it in production code.

> My problem with all these examples is that they are not at> all representative of "real" code. For example> > >val v = List("Hi ", "there!")> > Other than in unit tests, I'm hard-pressed to remember> ever wanting to write such code. An object will have a> list, and it will be read from a configuration file or> have a setter or something, but the times I'm wanted to> initialize it with two hard coded strings is, well,> never.> > In that case, I'm gonna have to write List<String>> explicitly, and Java seems much more reasonable typing> wise, and the benefits of more explicit type checking are> less of a burden to implement.> Well it would still be more concise with duh typing. In Java you might say:

As for dynamic typing, I find it hard to believe that it is more effective to find mistakes by testing than having them found by the compiler. (Also, I do not believe that the time saved by typing will be invested in writing unit tests.) So static type checking is good when you are working with complex data types (c++ templates, java generics, functional programming, ...). When you are using only simple data types, it will catch fewer mistakes and other aspects may be more important.

Another point: if the syntax does not distinguish between creation of a new variable and assigning to an exiting one, it is very easy to introduce mistakes by misspelling variable names.

I used to say that the sole value of static typing was intellisense support and refactor tool support. However, over the last release, we have been tightening up the type system in GScript (our internal, statically typed language), and every time we do, we find tens or hundreds of errors in our code. So we've seen first hand that static typing does, indeed, seem to prevent a non-trivial number of errors.

On the other hand, many of these errors were introduced by people who aren't full time developers, so there may be something to the assertion that static typing is less useful to advanced programmers. Unfortunately, my take is that there are many times more people who *think* they are advanced programmers than actually are. (I certainly don't know what I would do without static types.)

The problem with java is java, not static typing. Consider this bit of GScript:

but it is fully statically typed. We infer the types of "strs", "lens" and even the "s" argument to the closure for the developer. The cost of specifying an ArrayList of Strings is amortized over the entire context, rather than forcing the developer to constantly remind the compiler what is going on. The developer still gets intellisense everywhere, plus type and error sensitive refactoring tool support. When a developer really is concerned with the type of something, they can put the caret at that position and hit ctrl-T, and we show them the type at that point. Etc. etc.

Ugh. No wonder people are bailing from java to ruby. I've had to specify String three times and I can't abstract away the mapping algorithm without introducing an equally horrifying anonymous inner class, so I end up writing out the for loop and allocating the list of lengths myself. Since there is no nice list initialization syntax, I'm forced to add the strings one by one (or perhaps use an ugly Arrays.asList() hack or, if you are really clever, which I'm not, use a varargs trick.) No fun. Some may scoff at this as play code, but I write code like this *all the time* when I'm writing tests. (No laughing, fellow Guidewire engineers!)

So there are big benefits to static typing (most of which, I still believe, revolve around tools, but error detection provides real value as well), but this discussion should be divorced from a discussion of the *syntaxes* of statically typed languages which are, on the whole, pretty terrible.

I disagree that this is a stupid and useless warning. In fact it *should* be a compile error, and it would be if Sun weren't concerned that 1.4 code interoperate with generified 1.5 libraries without changes. Consider:<pre> List foo = new ArrayList(); foo.add(new Integer(1)); List<String> oops = foo; //warning, you've just pwnd the type system</pre>They could put some pretty specialized logic in javac (i.e. don't warn when the rhs of an assignment is a simple constructor of the generic type of an assignment to a parameterized version of that type), but that ignores that the constructor itself can do type system-damaging operations. At the end of the day, its a hole in the type system.

No such problem exists if you just let type-inference do its thing, plus you don't have to declare what type of list you are creating twice (or an upper bounding type that is of no interest within the local code block):<pre> var lst = new ArrayList<String>() </pre>Throw in a reasonable IDE, and now you are cooking with gasoline.

That being said, using IntelliJ, I usually just type "new ArrayList<String>()", then hit ctrl-alt-V to create a variable from the current expression, type in the variable name, hit enter and let IntelliJ deal with all the java boilerplate. IntellliJ does an incredible job of cleaning up the write side of the equation. Unfortunately, it can't do much for the read side.

> I used to say that the sole value of static typing was> intellisense support and refactor tool support. However,> over the last release, we have been tightening up the type> system in GScript (our internal, statically typed> language), and every time we do, we find tens or hundreds> of errors in our code. So we've seen first hand that> static typing does, indeed, seem to prevent a non-trivial> number of errors. > I'm curious what the context is that led your company to create an internal, statically typed language. I.e., why? From the looks of it, it works on the Java platform. Does it get compiled to Java source or bytecodes?

> Does Scala only have one type of list???>Well I'm still very much a Scala newbie, but my understanding is that Scala's main List is immutable and geared a bit towards functional programming. That's the one you get when you call the factory method List(). Scala also has Arrays, which are like mutable lists. Plus you can use the Java collections as well from Scala, which you might use when calling existing Java APIs from Scala.