Return to Home Page
      Blog     Consulting     Seminars     Calendar     Books     CD-ROMS     Newsletter     About     FAQ      Search
 

3-31-04 I'm Over It

Some very interesting analyses have come out of my discussions of latent typing. For example:

Pixel1
Pixel2
Pixel3

by an anonymous blogger named "Pixel" (no way to tell who it is from the site). Although there are places where Pixel misunderstood what I was saying (or more likely I didn't say it clearly enough), in general this states the "Java case" reasonably well.

That case includes the regular argument that people are just as productive in Java as they could be in Python, an argument which is almost universally made in the abstract, without any direct experience in both languages. I have regular experience in both languages, and the result is always: if you want to get a lot done, use Python. The testing doesn't come, as Pixel argues, with special hand-testing, but by using the actual data that you're trying to manipulate. You just get there faster, and start finding the real problems, using real data, faster. I have this experience over and over.

And the kinds of problems I solve are only theoretically solvable using Java. It would take at least 5-10 times longer to do it, and that assumes you wouldn't give up or get lost first. For example, you could translate something from Python, but it would be much easier because you had the Python design as a roadmap, and the resulting code would still be much bigger, messier, and (a big point of argument, since people regularly claim that the more verbose Java is easier to maintain because it's so much more explicit) harder to maintain.

In the follow up Pixel2, Pixel ends by asking "How much value do you put on checking as much as possible at compile time?" That oversimplifies the issue. If the type checking came at no cost, then the answer would be as easy and obvious as Pixel implies with his question. I've generally found that most folks who argue that strong static type checking must be preserved and increased at all costs have no experience of a down side because of it. And if you don't see any down side, then you think that arguing against it is obviously crazy.

I know because I began firmly in the strong static camp, having seen the benefits of moving from pre-ANSI C to C++. The C++ strong static type checking found lots of errors that (especially pre-ANSI) C didn't, so it was a clear win. And the extension of this philosophy to Java was thus obvious. But that was my only dimension of experience for many years. It was only when I began using a more dynamic, more succinct language (i.e. Python) that I got a new dimension. The experience most people have goes something like this: they have had enough exposure to Python to have it in the back of their mind. A problem arises that might require a one shot solution. Knowing how much effort Java is, they think that this is a good place to try Python, since it might save some effort. Then they have the watershed experience of solving the problem in far less time than it would take in Java. The next time a similar problem arises, they reach for Python more quickly, until they start using Python as a preferred tool, only using Java when there's no other choice.

During this process, the experience of being dramatically more productive in Python repeats itself over and over. It's not a philosophical argument (because the answer is "obviously" that the strong static language is going to be better), it's direct experience. The correctness and quality of the resulting programs is consistently very high.

Then you reach the intellectual vs. experiential crisis: you "know" that Java is "better," but your experience is that Python is "better." How can this be, when all the arguments that you "know" to be true clearly show that a strong static language must be "better." At that point you start trying to resolve this crisis by seriously questioning your preconceptions, as I have been doing. But the only arguments you hear back are the ones that you yourself (I myself) previously knew were true, and have been shown from experience to be uncertain.

What I'm trying to get to is that in my experience there's a balance between the value of strong static typing and the resulting impact that it makes on your productivity. The argument that "strong static is obviously better" is generally made by folks who haven't had the experience of being dramatically more productive in an alternative language. When you have this experience, you see that the overhead of strong static typing isn't always beneficial, because sometimes it slows you down enough that it ends up having a big impact on productivity.

I can't quantify this. I haven't been able to come up with a from-first- principles mathematical proof, probably because it depends on human factors, like how much time it takes to remember how to open a file and put the try block in the right places and remember how to read lines and then remember what you were really trying to accomplish by reading that file. In Python, I can process each line in a file by saying:

for line in file("FileName.txt"):
  # Process line

I didn't have to look that up, or to even think about it, because it's so natural. I always have to look up the way to open files and read lines in Java. I suppose you could argue that Java wasn't intended to do text processing and I'd agree with you, but unfortunately it seems like Java is mostly used on servers where a very common task is to process text.

There have been studies done on how much time it takes to recover from interruptions, but I can't make any direct connection other than to say that it seems to me that this must have a big influence, both when writing the code (yes, I know that much of it is automated with Eclipse and similar editors) and reading the code.

But the title of this article ("I'm over it") means this: Java is what it is. My discovery and reporting of the fact that Java generics doesn't support latent typing (which turns out to be the same thing that the Ruby folks invented "duck typing" to mean, not like prototypes where you add methods to objects, but just in the sense of "if it walks like a duck, don't worry about asking whether it's actually a duck." So Duck Typing is just the Ruby way to say "Latent Typing," a term that really does have history in computer science) was primarily meant to get people to show me how Java generics really do support latent typing in the event that I had misunderstood something. They didn't, and I didn't, so that's the case.

In the end, I've realized that the requirement that you have to use interfaces everywhere really isn't that big of a deal. In Python, which is a succinct language, it would really stick out, but Java is unashamedly verbose, and many people seem to like the verbosity and feel that it is a benefit. Requiring a few extra interfaces here and there is not much of an impact on the resulting code, and is indeed in keeping with the Java language philosophy (it would probably really surprise people if latent typing did have direct support in the language).

So I understand that Java generics are really just autocasting for nicer use of containers, and that's good and useful and it doesn't go any further than that. The generics chapter of Thinking in Java, 4e will thus be easier to write (nothing really complex will happen as in Thinking in C++, volume 2).

Finally, it is actually possible to do latent typing in Java, but you really have to want to do it because it requires more effort. Let's revisit the dogs and robots program one more time, this time using reflection to produce a method that uses latent typing:

import java.lang.reflect.*;

class Dog {
  public void talk() { System.out.println("Woof!"); }
  public void reproduce() {}
}

class Robot {
  public void talk() { System.out.println("Click!"); }
  public void oilChange() {}
}

class Mime {
  public void walkAgainstTheWind() {}
  public String toString() { return "Mime"; }
}

class Communicate {
  public static void speak(Object speaker) {
    try {
      Class spkr = speaker.getClass();
      Method talk = spkr.getMethod("talk", null);
      talk.invoke(speaker, new Object[]{});
    } catch(NoSuchMethodException e) {
      System.err.println(speaker + " cannot talk");
    } catch(IllegalAccessException e) {
      System.err.println(speaker + " IllegalAccessException");
    } catch(InvocationTargetException e) {
      System.err.println(speaker + " InvocationTargetException");
    }
  }
}

public class Latent {
  public static void main(String[] args) {
    Communicate.speak(new Dog());
    Communicate.speak(new Robot());
    Communicate.speak(new Mime());
  }
}

I can thus call speak() on anything, and it will only invoke talk() on objects that actually have a talk() method.

The output is:

Woof!
Click!
Mime cannot talk

Feedback

    Links I Read
Cafe Au Lait
Artima
Daily Python URL
Martin Fowler
Joel on Software
Paul Graham
Cringely
Search     Home     Web Log     Articles     Calendar     Books     CD-ROMS     Seminars     Services     Newsletter     About     Contact     Site Feedback     Site Design     Server Maintenance     Powered by Zope
©2003 MindView, Inc.