Angle Brackets and Curly Braces

Programming with "Duh" Typing

by Bill Venners

July 7, 2007



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.


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.

Guido van Rossum, creator of Python, said this in his interview:

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.

And Ruby creator Yukihiro Matsumoto (or Matz) said in his interview:

...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.

My take away from all these debates is captured in this quote from my Use the Best Tool for the Job article:

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:

...Don't check whether it IS-a duck: check whether it QUACKS-like-a duck, WALKS-like-a duck, etc, etc, depending on exactly what subset of duck-like behaviour you need...

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!' .

Similar code works in Ruby and Groovy. Here a Ruby version:

irb(main):001:0> v = ["Hi ", "there!"] => ["Hi ", "there!"] irb(main):002:0> v = v[1] => "there!" irb(main):003:0> v.upcase => "THERE!"

And here's a Groovy version:

v = ["Hi ", "there!"] v = v[1] println v.toUpperCase()

In Java, such an effort would be more verbose:

import java.util.List; import java.util.ArrayList; import java.util.Arrays; class Example { public static void main(String args[]) { List<String> v = new ArrayList<String>(Arrays.asList("Hi ", "there!")); String s = v.get(1); System.out.println( s.toUpperCase() ); } }

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:

scala> val v = List("Hi ", "there!") v: List[java.lang.String] = List(Hi , there!) scala> val s = v(1) s: java.lang.String = there! scala> s.toUpperCase unnamed0: java.lang.String = THERE!

In the first line, I call a factory method named List , passing in two String s. 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.

Talk Back!

Have an opinion? Readers have already posted 370 comments about this weblog entry. Why not add yours?

RSS Feed

If you'd like to be notified whenever Bill Venners adds a new entry to his weblog, subscribe to his RSS feed.

About the Blogger

Bill Venners is president of Artima, Inc., publisher of Artima Developer (www.artima.com). He is author of the book, Inside the Java Virtual Machine, a programmer-oriented survey of the Java platform's architecture and internals. His popular columns in JavaWorld magazine covered Java internals, object-oriented design, and Jini. Active in the Jini Community since its inception, Bill led the Jini Community's ServiceUI project, whose ServiceUI API became the de facto standard way to associate user interfaces to Jini services. Bill is also the lead developer and designer of ScalaTest, an open source testing tool for Scala and Java developers, and coauthor with Martin Odersky and Lex Spoon of the book, Programming in Scala.

This weblog entry is Copyright © 2007 Bill Venners. All rights reserved.