{ by david linsin }

June 23, 2008

Static Typing with Less Keystrokes

In my last blog I wrote about the more concise syntax of "dynamic languages". In my example I used Scala to highlight the differences. Daniel pointed out in the comments, that Scala is not dynamically typed. In fact it is the opposite, a statically typed language. I think I need to clarify, that I misused the term "dynamic languages" as a synonym for "new kids on the block languages" like Groovy, Ruby or Scala.

The latter has a feature called type inference, which rose quite some discussion in the Java community. Wikipedia explains it as follows:

Type inference, or implicit typing, refers to the ability to deduce automatically the type of a value in a programming language. It is a feature present in some strongly statically typed languages. It is often characteristic of — but not limited to — functional programming languages in general. ... The ability to infer types automatically makes many programming tasks easier, leaving the programmer free to omit type annotations while maintaining some level of type safety. Explicitly converting to another data type is called "casting" (or a "cast").

After reading this definition, I immediately asked myself: Are we too lazy? All this does is save some typing? We have our IDEs which, in some sense, already infer types for us. For instance they suggest the correct parameters when calling a method, based on which type you need to pass. Doesn't type inference just sound like a "nice to have" feature of a programing language? Is it really valuable?

Ola Bini for instance claims that type inference could save some typing in Java, but probably not more:

Type inferencing would probably not do much more than save you some typing. But how much typing it would save you could definitely vary depending on the type of type inference you added.

Well, to be honest I am a little lazy these days. Anything that saves me some keystrokes in Java and still keeps my code readable and thus maintainable is very welcome. I don't expect fancy stuff. I just think something like the following, would be quite neat:


Map<Integer, String> keyVals = new HashMap<Integer,String>(); // duplicated type declaration
Map keyVals = new HashMap<Integer,String>(); // with type inference Map becomes Map<Integer, String>
Map<Integer, String> keyVals = new HashMap(); // with type inference HashMap becomes HashMap<Integer, String>
Map<? extends Number, String> keyVals = new HashMap<Integer,String>(); // if needed add generic type declaration


I'm not a language expert or compiler designer and I'm pretty sure I'm missing a lot of corner cases, but I guess implementing this kind of type inference should not be impossible. In fact, Ola Bini has a similar example in the aforementioned blog post.

The second statement of my example above, actually compiles in in Java today. You'll probably get a warning by your IDE for using a "raw type", but that's about it. The difference with type inference is that the compiler will check whether you use a correct typed key/value pair:


Map keyVals = new HashMap<Integer,String>();
keyVals.put("1", new Integer(4711)); // works today, with type inference compiler will know it won't


In a recent blog post Jeff Atwood wrote about how C# takes it even one step further. You can completely omit types on local variables and let the compiler do its magic. In Java it could look like this:


var keyVals = new HashMap<Integer,String>();
keyVals.put("1", new Integer(4711)); // compiler knows that this is not allowed


This would be nice, but I'm not even asking for that. I simply would love to see, that the compiler infers the generic type of key and value of a Map, so that I have the same type safety as with a generified Map in Java today. I think you would get the best of both worlds: static typing with less keystrokes.

8 comments:

Daniel said...

From a theoretical standpoint, a static type system with dynamic capabilities may as well be completely dynamic, because it can no longer *guarantee* soundness at compile-time. This is the big problem I have with languages like Fan or with people who try to use Groovy's static typing and claim that it makes their code more correct.

From what I've seen in my experience with languages such as Scala and ML which have static type inference, it's about the best possible solution - at least to my way of thinking. Minimal effort is expended in needless type annotations, redundancy is all-but eliminated (especially in ML), and really lovely design patterns open up that would be either too verbose or simply impossible in other languages. Don't get me wrong, I love languages with dynamic type systems, but I'm quite fond of all the information my compiler can infer.

Getting back to my original point though, I honestly don't see the point to a hybrid dynamic/static type system. If you're going to do that, then why not go all out and make the whole thing dynamic? In such a type system, the only purpose static type annotations serve is documentation, a purpose which is more readably served in comments or wikis, not code.

Greg said...

More than just preventing keystrokes, it prevents redundancy. Why specify the type when you've already specified the code itself? Redundancy usually means more chance for errors (DRY!). But sometimes redundancy can be a failsafe. (and sometimes adding annotations to narrow your interfaces lets you draw the proper distinction between interface and implementation) Only with static type inference do you get to make the decision yourself which redundancy is good and which is bad.

david said...

daniel > Getting back to my original point though, I honestly don't see the point to a hybrid dynamic/static type system. If you're going to do that, then why not go all out and make the whole thing dynamic?

Well that's the thing I don't get about Fan. Why would I need a dynamic and static type system? Where would that be useful?

daniel > In such a type system, the only purpose static type annotations serve is documentation, a purpose which is more readably served in comments or wikis, not code.

Well I'm not sure I fully agree on that. I ran into situations where it was really helpful to have those type annotations. When scanning through a huge pile of code looking for that nasty bug, it can be quite helpful to know what type you re dealing with.

greg > More than just preventing keystrokes, it prevents redundancy. Why specify the type when you've already specified the code itself? Redundancy usually means more chance for errors (DRY!)

Another reason for me to advocate laziness and type inference!

Anonymous said...

I think people who like dynamic typing because it means they have to type less are pennywise and pound foolish. Hint: Typing the code is not the longest part of the development cycle.

More or less I think people haven't read the documentation for Eclipse or are using a crummy syntax highlighter and found out how there are 100s of little things that Eclipse does to make working with a statically typed language a real joy. Refactoring, ridiculously smart code completion, code style and bug pattern analysis. The list goes on and on.

Sure if you are using vi to edit your Java code it will be less productive than other languages but that's a developer personality problem not a programming language problem.

Anonymous said...

anonymous, I think the only people who use dynamic languages because they "type less" are people who have never spent more than a few hours or days using one.

Dynamic languages weren't invented to change:
int foo = 5;
into:
foo = 5;

This is a common misconception I hear a lot. And I understand why people would think this coming from a static typed background. What I try to explain to people, is that dynamic typed languages were designed to simplify and expand on the concepts such as interfaces, generics, abstract classes, and meta-programming. If you understand why there is an Object type and Generics in Java, then you understand why dynamic languages exist.

I normally some it up this way: When you program in a dynamic language, every class you program uses generics, and has automatic interfaces.

david said...

anonymous > I normally some it up this way: When you program in a dynamic language, every class you program uses generics, and has automatic interfaces.

Could you elaborate on this? Sounds interesting, although I don't think I fully get it.

Daniel said...

david > Could you elaborate on this? Sounds interesting, although I don't think I fully get it.

In a dynamic language, generics are completely unnecessary because no extra typing is required to treat some arbitrary object as a String (for example). Generics are very much a figment of static typing, required to parametrize existing classes across different types, allowing properties of these classes to be accessed in a type-specific manner. Consider the following example in Java:

List<String> names = Arrays.toList("Daniel", "Chris", "Joseph");

String me = names.get(0);
me = me.trim();

for (String name : names) {
    int len = name.length();
}

Compare that to the (roughly) equivalent Ruby:

names = ["Daniel", "Chris", "Joseph"]

me = names[0]
me = me.strip

names.each do |name|
  len = name.length
end

In Java, we are required to parametrize the List type in order to treat its elements as of a type more specific than Object. Ruby does not require this, because we can treat *any* instance as if it were of any type. So long as the instance in question defines the methods accessed (and we know that they do in this case), then there is no problem.

The "automatic interfaces" point is similar in nature, but I think more related to structural typing. In Java, we program to the interface so that we can treat different objects polymorphically on the methods they define. The interface provides a class-level contract, ensuring that those methods are indeed well-defined. In dynamic languages, there is no need to enforce this contract, just use the method and trust that it does indeed exist (duck typing). Scala can do something very similar with structural types, but it's not quite as flexible.

david said...

Thanks for pointing that out Daniel!

Since I'm not a native english speaker the term "generics" tricked me. When I think of "generics" I immediately think of specifying a type or narrowing down a type of e.g. Java Collection. The opposite of what generic actually means.

Comparing structural typing with an interface makes perfect sense. However, I guess it's just a matter of taste, if "trusting" that the method might be there at runtime is enough or letting the compiler tell you that it's definitely gonna be there. I don't know, maybe I'm still struggling with whole dynamic mindset... or I'm just in love with the compiler.


com_channels

  • mail(dlinsin@gmail.com)
  • jabber(dlinsin@gmail.com)
  • skype(dlinsin)

recent_postings

loading...