Fear and Loathing in Typeland

by Robert Cooper

Pat throws down the proverbial gauntlet with Java People Must Be Stupid. It is a couple of bodily fluid references away from being a Hani post. In short, he discounts the need for strong typing in Ruby:

How do I make Ruby safer? It's a topic that people bring up on the mailing list every once in a while. People shouldn't be able to open my classes. They need compile time type checking so the app doesn't blow up. Extending system libraries is bad.

Seriously, browse through ruby-lang for threads where the starter needs Ruby to be more "safe." A ruby hacker will cheekily ask, "Why do you need static type checking? Are you going to try to regexp match an Animal?" "Well, no, not ME of course, but somebody else might." I don't even know who "somebody else" is most of the time. Is it someone on your team? Is it someone from another department? Is it some random guy who downloaded your library?

Concerns like these all seem to boil down to one major theme: The people I work with are stupid.

[snip]

The more serious issue is when "someone else" refers to someone whose welfare you actually are interested in, or, god forbid, responsible for. If you need to protect someone on your team from code, there are two scenarios in play:

1. The person is stupid
2. The person is naive

Neither issue is really that difficult to handle. The first one has a very simple solution - fire them. You heard me. If you've determined that this person truly has no chance of learning how to judiciously use some piece of code, there's zero chance they'll had value to your project. Programming is a vigorous intellectual exercise, and I've never seen incompetence actually benefit such endeavors.

[snip]

Ruby doesn't need to be more safe. It certainly doesn't need static type checking. What we need is to kick the idiots out, and educate those with less understanding. Doing so improves those individuals, ourselves, and our craft as a whole. It doesn't take much more effort than trying to keep people from shooting themselves in the foot and performing damage control when they find workarounds. The benefits are obviously way better. So what's stopping you?



Now. Let me be clear here. Static typing isn't an end-all-be-all of making your language safe, and certainly not foolproof. But there are some important things to consider here.

1. If you are creating an API for other people to use, typed arguments and returns make your code (closer to) self-documenting.

One of the things that kills me about the Ruby people is more than a few times when someone asks about the types going into and out of API type methods, the answer is "Write a test case." This strikes me as the apex of lazy on the part of the API developers. You didn't document it, and the language doesn't give me good hints, so it is my problem to figure out how the method works by writing tests. Like I want to test someone elses code. Frankly, this kind of thing pervades Ruby as a problem. The JRuby guys are constantly bumping up against the fact that Ruby has no language spec (though the Wikified spec process has recently begun) which means they have to test and replicate every edge-case behavior in the language. You carry this forward into APIs and, idiot or not, you are making more work for other people.

Now, I am a fan of duck-typing. Indeed, support for duck-type adapters in Java is one of the reasons I have wanted Java to have Dynamic Proxy support for concrete objects for so long. I hope that language level property accessors will open up this possibility as the entire front-side of an object will be definable in interface form. However, feed( Duck ) is still easier to figure out than feed( Object ). In a dynamic language, you don't even have to enforce it, but having it simply expressed -- as you can see in ActionScript -- helps a lot for people using your code.

2. Extending core classes is a security problem.

One of the powers of dynamic languages is that you can runtime alter objects. (As an aside The Crockford Lectures at Yahoo on JavaScript are great) This is powerful, and in the scope of the kind of work being done in Ruby, or even the still basic AJAX stuff in JavaScript, it is all well and good. There is a reason, however, that String and Integer are final classes in Java: because in the C++ world, the ability to replace the String implementation in a library was one of the most common vectors for Trojan/Malware code to extract things from a secure system.

When you work in a large enterprise with many teams working on many internal applications and internal APIs that those applications use with a mix of security modes for each of them, opacity is sometimes important. More important, however, is making sure you can't inject a String that, say, emails passwords to some account, or at runtime clobber the behavior of a policy-managed class. In this case, having ENFORCED typing is actually important. While there are certainly large Ruby apps roaming around out there, I haven't seen any discussion of multi-team/multi-project groups in an Enterprise dealing with these issues yet. Isolation via WS-* calls is an option there, but sometimes that is less than practical.

3. Static Typing makes large-scale automated refactoring possible.

As a GWT fan, one of the things that people keep looking at Java for is the tooling. While Tor seems to be making great strides in the Ruby support, automated refactoring in untyped languages is hit and miss at best, completely impossible at worst. For my money, good refactoring tools in the hands of the average developer has been one of the great leaps forward in development over the last decade, and it is a big encouragement to have better APIs and internal code. Loss of this is not something I would like to see on a large scale. When you are talking about multi-project-multi team, losing the obvious checks against code that gets a new version of an API is a problem, even if the refactoring works as advertised in the API project. Again, you are back to writing test cases against someone else's API to see if there are obvious breakages.

All these points aside, I won't dispute that there are a lot of bad developers out there. I am certainly not going to call Ruby people stupid, but I think there is a mismatch here. I like Ruby, and it obviously has big advantages in certain scopes, but those advantages cost it in other areas. Seeing people advertise it as the end-all-be-all language without admitting the places where it is worse than what we have in other language is a staggering level of arrogance.

22 Comments

but the cost
2007-01-31 20:59:50
Static typing can catch some bugs and more importantly can allow us to exploit sophisticated static analysis for a beautiful IDE experience (I adore IDEA), but I firmly believe many static typers just aren't aware of the cost of static typing. The requirement for type safety, and the consequent loss of duck typing, lead to whole designs being avoided or otherwise negatively influenced to accommodate coarse grain interfaces. For example, static typing pretty much precludes a delegate UI model (like Cocoa), which is far superior to subclassing your mother's uncle. Many large, sophisticated applications were written in Smalltalk before Java came along, and it's rare to find a Smalltalker who wouldn't revert given the chance. Why is that, do you think? I have 10 years full-time Java experience, and I quite like the language, but I'm constantly aware of the loss of flexibility it's use implies.
cooper
2007-02-01 02:43:18
I don't think type safety necessarily precludes ducktyping (in effect anyway). There is an old example of using a quick adapter factory to wrap an object with an interface needed as an argument more a method. It is all well and good, but having interfaces everywhere in API code becomes annoying and problematic for a number of reasons I don't want to get into.


However, looking at "the future", if we have property accessors and can create DProxies from concrete objects and closures for providing missing implementations, I just don't see that Java would lack anything in terms of capability that Ruby (or Objective-C for that matter) have. Even better, you can have the "code level" features without losing the tooling and analysis capabilities that static typing gets you.


That said, though, I can't help but note what a big boon the "beautiful IDE experience" is. I was an emacs+make/ant guy for years and years and I look at the current crop of Java IDEs and I can't even imagine going back to the old days now. I must admit, though, I was never a Smalltalker. Aside from toying with Squeak, my language trajectory went something like Modula in highschool, LISP/C++ in college and the VC++/VB to Java in "the world".

David Smiley
2007-02-01 08:51:47
Great post! I think we should also keep in mind newer languages like Scala and Boo which are strongly typed but have type inferencing. That feature tempers some of the arguments duck-typing fans have against strong typing.
Mark Murphy
2007-02-01 09:27:07

Adding static type checking to Ruby to force APIs to be documented is akin to requiring all Java programs to run solely on Windows in order for timezones to work. If the APIs aren't documented, set a project standard to require them to be documented. Period. You don't need a language change to support that.


Object#freeze in Ruby accomplishes much of what the final keyword does in Java.

Frank Wilhoit
2007-02-01 09:45:06
Strong typing is like violence: if it isn't solving all your problems, you must just not be using enough of it.


Type sloppiness--deliberate or, even worse, unwitting--is at the root of very nearly all logic and semantic errors.


Every aspect of behavior is an aspect of type. Literally every single machine instruction that your code executes ought to be interpretable as part of the semantics of some type. Every business rule is part of the semantics of some type. And so forth, to every level of every solution and every platform.


What is needed is far stronger typing than has ever been envisioned or attempted. This goes to the level of the processor as well: where, for example, is the chip with unsigned integer registers, such that an attempt to decrement zero stops the task with a hardware exception?


A good program should consist almost entirely of constructor calls. The definition of each type must dictate its entire lifespan from construction to (synchronous!!!) destruction. Its behavior is its view of the processes that it participates in.

cooper
2007-02-01 10:04:29
Adding static type checking to Ruby to force APIs to be documented is akin to requiring all Java programs to run solely on Windows in order for timezones to work.


In what way? Execution platform has nothing to do with easily discoverable design time contracts.


Not roll into "Strong typing is like violence: if it isn't solving all your problems, you must just not be using enough of it," but I keep wishing Java had MORE typing and contracting options, not less. The whole discussion about XML-in-the-language support in Java seems silly to me. If we had a better property vs attribute system and a typing system that more closely matched XSD/RelaxNG -- making the FindBugs/JAX-B annotations actual language structures and not annotations -- we would have a better system.


These are two different sphere's here. Most of the discussion about "project rules" for documentation is a canard. One of the best things Java has going for it is JavaDoc and easy documentation. Saying you should have externally documented API contracts in Ruby as a "policy" means that the "policy" will always be the exception, not the rule. And like most things in the Ruby space, the documentation will lag significantly behind the actual code operation anyway.


Object#freeze in Ruby accomplishes much of what the final keyword does in Java.


Yes, but Object#freeze doesn't assure that you aren't going to pass into a security-critical API an "Object" like object that is masquerading. Imagine, if you will, something like an encryption library with a scheme class getting passed in. I don't want you to pass me a Duck that emails my keys to your hotmail account in place of the SecureDuck that is frozen. It might still be a duck, but you have now rolled back to all the nightmares of securing C++ code.

Trevor
2007-02-01 10:23:27
There is a reason that String and Integer are final classes in Java: because in the C++ world, the ability to replace the String implementation in a library was one of the most common vectors for Trojan/Malware code to extract things from a secure system.


Are you sure this is the reason? I don't see how replacing a non-final String class could be possible in Java, not even for malware.


Also, simply making String, Integer, and a few other classes doesn't solve the problem anyway. There a host of other API classes that are not final (e.g., java.io.File), and malware could simply replace one of them.


I was told that the real reason String is final is simply for performance. Calling a method in a final class is faster than for a non-final class. And since String is so commonly used, presumably there is an overall boost in performance that it is worth making String non-extendable.

cooper
2007-02-01 10:35:20
Every aspect of behavior is an aspect of type. Literally every single machine instruction that your code executes ought to be interpretable as part of the semantics of some type. Every business rule is part of the semantics of some type. And so forth, to every level of every solution and every platform.


What is needed is far stronger typing than has ever been envisioned or attempted. This goes to the level of the processor as well: where, for example, is the chip with unsigned integer registers, such that an attempt to decrement zero stops the task with a hardware exception?


Aaah the DSL evangelist! :)



Are you sure this is the reason? I don't see how replacing a non-final String class could be possible in Java, not even for malware.


It is not the only reason, but it is one of the reasons. Things like "File" are always inherently "untrustworthy" though. If you pass me a File object, you already control that file and can do anything I might do to it anyway. If you can pass me a String implementation that can propagate itself via subcalls like substring and match, then you can actually "infect" a system with code. I have always considered it unfortunate that Java has never provided an immutable array at the language level too. A lot of security related code ends up having to do tons of copies of arrays to make sure you can't inject a mutable, extendable class into a secure API.

Frank Wilhoit
2007-02-01 11:09:31
DSL evangelist? Only in the sense that every program is a DSL, implemented in a metalanguage such as Java, C++, or pick-your-poison. Or what if "DSL" stood for "Domain-Specific [type] Library"?
cooper
2007-02-01 11:13:59
Well, "Literally every single machine instruction that your code executes ought to be interpretable as part of the semantics of some type. Every business rule is part of the semantics of some type. And so forth, to every level of every solution and every platform." is pretty much the argument for the people espousing automated Domain Specific Language generation. You spend most of your time crafting the DSL, where everything down to your core operators becomes a type in the definition, then your actual "program" is just correlating the relationships between the types in operational processes. In effect, doing away with the general purpose programming language for "business" development.


What you are saying goes a whole lot deeper into the core than that, though.

Frank Wilhoit
2007-02-01 13:28:54
To me, a domain-specific language is nothing more than a fresh set of learning curves, the necessity for which faces a heavy burden of proof. Domain-specific libraries as a way of thinking about applications--which I just then thought of, while composing that last post--seems like a fascinating notion; but it is late in the afternoon here and I may just be getting punchy.
Bill Siggelkow
2007-02-01 13:34:29
Ruby *is* a strongly typed language unlike VB which is weakly typed. For a good discussion on type systems see http://en.wikipedia.org/wiki/Type_system. I'd also like to point out that types can only explain half (or less) of what an API provides. Have you ever used an API where the JavaDoc had no comments? How did you find out what it did? You wrote some test code -- that's how. Static typing does not ensure that the API is well-documented. Furthermore, development has become so complex these days that in addition to the third-party APIs, you have to deal with a mish-mash of XML configuration files, tag libraries, properties files and the like.
chromatic
2007-02-05 11:57:52
Static Typing makes large-scale automated refactoring possible.


Heh, at least you admit you never used Smalltalk.


You might also say that one of the things that prevents people from feeling Java's painful lack of useful metaprogramming capabilities is the tooling.

cooper
2007-02-05 12:25:10
I'd also like to point out that types can only explain half (or less) of what an API provides. Have you ever used an API where the JavaDoc had no comments? How did you find out what it did? You wrote some test code -- that's how.


Actually, just last week. The Maven 2 code is HORRIBLY documented. Working on the GWT Mojo files, I found everything to be pretty straightforward, right up until they started using collections of... What? I found myself cursing the lack of generics usage in M2 because you can't tell whether the Resources and CompileRoots contain Files, or Strings or (in the one case) the "Resource" object (which isn't even in the same package or library as the "Project" object. However, everything else being equal, the fact that there are declared types most places makes working with it much easier.


You might also say that one of the things that prevents people from feeling Java's painful lack of useful metaprogramming capabilities is the tooling.


You might also say the thing that prevents people from feeling Ruby's painful lack of type safety is the lack of APIs and Rails "current" pattern usage. Refactoring a large application without that kind of tooling is truely a nightmare.

alpha3000
2007-02-05 17:56:04
Who the hells care about ruby in a Java website, Im so tired of reading ruby this, ruby that, its just a shaky scripting language if you want a real OOP language use smalltalk not ruby thats for wimps.
chromatic
2007-02-08 13:39:30
You might also say the thing that prevents people from feeling Ruby's painful lack of type safety is the lack of APIs and Rails "current" pattern usage. Refactoring a large application without that kind of tooling is truely a nightmare.


I agree. It would be--if applications written in dynamic languages were the size of their equivalent applications written in static languages.


Fortunately, it's easy to beat Java on expressibility with a language which supports metaprogramming. Strangely, plenty of programmers have built plenty of great systems without even noticing a "painful lack of type safety" in dynamic languages. I'm not sure why you're changing the question to a long-dead and well-debunked myth, but there you go.

Kurt Cagle
2007-02-08 14:25:16
Coming from the XML world, the need for strong-typing has always rather bewildered me. In the context of XML, type is basically an abstraction on a given data structure to provide validity, but little more, and that type can as a consequence be expressed in a very mutable and fluid variety of formats. The danger with strong typing comes from the complexity that such type adds into the build process (there are literally dozens of stream manipulators, for instance, and each of these stream types opens up the potential for incompatibilities and "edge" case programming that makes development complex and painful for most developers, and tends to lead towards Framework ossification as a natural byproduct.


Strong typing also imposes a secondary headache - if you have perhaps a dozen core classes, API management remains feasible, but once you start building class after class after class, each with its own subtle variations, then the benefits of such typing systems begin to pale compared to the complexity in integrating the classes together, version control and code reuse. This inevitably makes such class systems fragile, especially as different agencies may have responsibility for different pieces of the application code.


I am not dissing OOP methodologies here - there are clearly benefits to OOP structures. The danger comes when the requirements necessary for the hosting of those structures imposes an undue barrier on the overall application, due to code complexity, limited integration endpoints, and deep inheritance trees. That's why I'm inclined to see greater benefit in weakly typed languages. To me, statically typed languages become more of a crutch for the developer than dynamic ones do, and solid architecture (and documentation) can generally solve the more egregious problems that dynamic types are often accused of (and that static-typed languages are typically almost as prone toward).

Kurt Cagle
2007-02-08 14:27:18
Coming from the XML world, the need for strong-typing has always rather bewildered me. In the context of XML, type is basically an abstraction on a given data structure to provide validity, but little more, and that type can as a consequence be expressed in a very mutable and fluid variety of formats. The danger with strong typing comes from the complexity that such type adds into the build process (there are literally dozens of stream manipulators, for instance, and each of these stream types opens up the potential for incompatibilities and "edge" case programming that makes development complex and painful for most developers, and tends to lead towards Framework ossification as a natural byproduct.


Strong typing also imposes a secondary headache - if you have perhaps a dozen core classes, API management remains feasible, but once you start building class after class after class, each with its own subtle variations, then the benefits of such typing systems begin to pale compared to the complexity in integrating the classes together, version control and code reuse. This inevitably makes such class systems fragile, especially as different agencies may have responsibility for different pieces of the application code.


I am not dissing OOP methodologies here - there are clearly benefits to OOP structures. The danger comes when the requirements necessary for the hosting of those structures imposes an undue barrier on the overall application, due to code complexity, limited integration endpoints, and deep inheritance trees. That's why I'm inclined to see greater benefit in weakly typed languages. To me, statically typed languages become more of a crutch for the developer than dynamic ones do, and solid architecture (and documentation) can generally solve the more egregious problems that dynamic types are often accused of (and that static-typed languages are typically almost as prone toward).

cooper
2007-02-09 21:09:16
You might also say the thing that prevents people from feeling Ruby's painful lack of type safety is the lack of APIs and Rails "current" pattern usage. Refactoring a large application without that kind of tooling is truely a nightmare.


I agree. It would be--if applications written in dynamic languages were the size of their equivalent applications written in static languages.


Fortunately, it's easy to beat Java on expressibility with a language which supports metaprogramming. Strangely, plenty of programmers have built plenty of great systems without even noticing a "painful lack of type safety" in dynamic languages. I'm not sure why you're changing the question to a long-dead and well-debunked myth, but there you go.


My point was, you were recycling a long dead myth about Java, and swapping cliches isn't worth either of our time..


Everyone seems to think I am part of the "Ruby basher" set, and I am really, really not. My only point here is that Ruby isn't the end all be all. Neither is Java. But until we stop this holy war, we aren't going to see the difference.

cooper
2007-02-09 21:14:49
Coming from the XML world, the need for strong-typing has always rather bewildered me. In the context of XML, type is basically an abstraction on a given data structure to provide validity, but little more


Yes, I can see how... validity.. might be confusing..


Seriously, WTF here? Frankly, I would love to see more type enforcement in Java that matches XSD or RelaxNG, not less. Frankly, turning Java into the "Web Service" language. The idea that "from the XML world, you would, what, like to see more confusion between xsd:nill and absent in datastructures?


You want character limited properties on objects to no be enforces?


I want XML specificity IN Java objects, not some hackneyed "tags in the language" syntax. To somehow imply that less type specificity, not more, would lead to this seems like some serious crack smoking.

Chris Treber
2007-02-13 03:34:46
Good writing. I really like Pearl. But it happened to me a couple of times when code became big and a mess, odd errors crept up, and I had to rewrite the whole thing in Java. In Java just more errors are cought, and the IDE can use string typing for better "guess by signature" etc. functions.
Mike Sullivan
2007-02-22 10:30:06
Sorry but I'm sure there are some decent Rubyists out their hiding their heads in embarrasment as zealots like "Pat" pile on the band wagon with brilliant advice such as : "It certainly doesn't need static type checking. What we need is to kick the idiots out"


Ok, "Pat", you're 100% convincing there, great argument. When you don't know anything, when you don't have anything to say, just call others stupid!