Tuesday, May 29, 2007

Are Open Classes Evil?

When doing my JRuby talk at No Fluff, Just Stuff, one of the consistent questions I get is the morality question about open classes: are they evil? Open classes in dynamic languages allow you to crack open a class and add your own methods to it. In Groovy, it's done with either a Category or the Expando Meta-class (which I think is a great name). JRuby allows you to do this to Java classes as well. For example, you can add methods to the ArrayList class thusly:
require "java"
include_class "java.util.ArrayList"
list = ArrayList.new
%w(Red Green Blue).each { |color| list.add(color) }

# Add "first" method to proxy of Java ArrayList class.
class ArrayList
def first
size == 0 ? nil : get(0)
end
end
puts "first item is #{list.first}"

Here, I just crack open the ArrayList class and add a first method (which probably should have been there anyway, no?). When you define a class, Ruby checks its classpath to see if another class of the same name has already been loaded. If it has, it adds the new behavior to the class.

It it's too frightening to add a method to the entire class, Ruby gives you the option of adding it to an object instance instead. Consider this:
# Add "last" method only to the list object ... a singleton method.
def list.last
size == 0 ? nil : get(size - 1)
end
puts "last item is #{list.last}"

Here, I add the last method just to the instance of the list class (i.e., this list object). That way, you don't add it to the entire class.

Many Java and C# developers are shuddering in horror right now. The consensus seems to be that this is just too dangerous. And, like all advanced language features, it can be abused. But here is a counter argument. How many Java and C# developers have a StringUtils class in their project? Pretty much everyone. Why? Because the language designers for both languages no only won't allow you to add your own methods to String, they won't even allow you to subclass it to create your own String class. Thus, you are forced by the language design to switch from object-oriented coding to procedural coding, passing Strings around like you are writing in C again.

Open classes allow you to make your code much better, when used responsibly. One common argument against this feature from paranoid developers is that they don't trust "junior" developers with this kind of power. So, tell the junior developers on the project not to do it! And, if you really hate the additions that have been made, you can also reopen a class and remove methods at runtime, using either remove_method (which removes it from a class) or undef_method (which removes it from the entire inheritance tree).

You can add new methods to classes in Java if you want to use Aspects, but:
  • uses an entirely different syntax from Java

  • you give up tool support in your IDE for the new methods (in fact, it probably won't even compile it properly without some plugins and such)

  • it's so much trouble that you just suffer through the ugly, non-OOP StringUtils class


In JRuby, you can add methods to existing classes:
  • using the natural syntax of the language

  • it is supported as much as anything else (we don't have IntelliJ for Ruby...yet), but nothing chokes either

  • it is so easy that it is natural


I think this level of power makes developers used to non-dynamic languages queasy. But in the right hands, it can make you code much more expressive, and not pollute your namespaces with lots of made up class names with Util and Helper tagged on the end. Like all advanced features, when used correctly, it makes your code much better. Are open classes evil? No more evil than any advanced language feature.

Monday, May 21, 2007

Cruising

This is old news to some, but Ruby now has a great continuous integration story: cruisecontrol.rb. Written by a bunch of Really Smart Guys, it has some innovative features (convention over configuration, no XML, DSL-style tasks, and many more). I mistakenly said in my aboutGroovy Podcast that cruisecontrol.rb supplants the other versions of CruiseControl, but that was clearly wrong: Enterprise CruiseControl was announced recently, which provides consulting, 24x7 support, and new features.

CruiseControl is an example of the best kind of tool: one extracted from real need on a project. The original CruiseControl was developed because developers on a project were feeling build pain and came up with a great solution. Tools are always better when they are derived from real-world experience. Another great example of this is Ruby on Rails. DHH didn't wander over to an ivory tower and think about what a framework should look like. He built some web applications and extracted a framework. Getting developers together to invent frameworks and tools never works as well as taking working stuff and distilling frameworks and tools from it. The classic easy target of how not to do it is EJB 1 and 2: get a bunch of vendors in a room and figure out what developers need (or, more likely, what do we have to sell that we can convince developers they need).

CruiseControl is an ongoing illustration of what happens when you get clever people together who can not only spot a problem but come up with a top notch solution to fix it, then distill the results.

Tuesday, May 15, 2007

The Importance of Language

Language is important.

One of the purposes of my last two blog posts was to use hyperbole to make a point. And, judging from the hue and cry, I was successful in irritating a lot of people. To do that, I used 2 purposefully inflammatory terms: "communist" and "bureaucracy". As many people pointed out, communism as a form of government is clearly a bad thing, as proven in very expensive social experiments in the last century. Bureaucracy, though, merely has a negative connotation because it tends to be misused. In very large groups of people, some level of bureaucracy is necessary to preserve order. It is in the arbitrary application of bureaucracy that earns it a negative connotation.

When you read the first sentence of this post, you probably though I was referring to computer languages, not English. That's a perfect example of how spoken languages are sometimes ambiguous in useful ways. In fact, poetry is an art form that takes good advantage of the imprecise nature of our spoken languages. It is in fact hard to create a useful human language with very strict, unambiguous rules (as Esperanto showed). I'm sure lawyers would love to have a language where nothing is ambiguous; it seems like a large part of contract negotiations concerns removing ambiguities and trying to craft real, objective meaning from our incredibly malleable language.

Computer languages are important in just the same way as spoken languages. When creating a bureaucratic, legalistic system, strong contracts and unambiguous language matters. Very large systems benefit from bureaucracy. Using Java, C#, or some other statically typed language for building incredibly large applications takes advantage of the useful bureaucracy inherent in the language. I used to think that this serious hampered the ability to build large, complex systems in dynamically typed languages. You necessarily remove some of the useful ambiguity from such systems.

Some of the richness of spoken language hinges on purposeful vagueness. This means that syntax matters (in both spoken and computer languages). Ultimately, computer languages help us operate Turing machines, which means that any Turing-complete language will allow you to do anything you want, contingent on effort. As Paul Graham aptly states, you could build all of the features of Lisp using C, but the shortest route would be to build a Lisp interpreter in C, then write your Lisp code on top of that.

That suggests that the choice of language depends on the useful tension between inherent capabilities, expressiveness, rules, and the productivity because of the interplay between these factors. This helps explain why there are thousands of computer languages, each finding their own combination of those factors. Paul Graham has a great essay about languages, power, and suitability called Revenge of the Nerds.

Computer languages are just tools, and I try not to get religious about tools (my inflammatory posts for effect to the contrary). I personally have changed my views over the last few years about the efficacy of dynamically typed languages like Ruby, even for large enterprisey applications, as long as you have strong testing to back you up. The flexibility of the meta-programming in Ruby allows you to solve hard problems in elegant ways that are either too cumbersome or just flat impossible in a more limited language (even if those limitations are on purpose). Rails is a great example of a framework builder pushing the really powerful features of the language and in the process creating a framework that you don't need to know all those things to operate. Limiting the power of the language hurts your best programmers the most. The interesting question is: given the productivity difference between the best and worst developers, how much to you want to limit your best developers at the expense of "protecting" the weaker ones? And, before you think that bureaucratic languages make bad developers safe, consider the great quote by Glenn Vanderburg (based on his experiences in the real world) : "Bad developers will move heaven and earth to do the wrong thing."

Thursday, May 10, 2007

Static Typing & Bureaucracy Redux

As I expected, there is serious dissension in the ranks over my post claiming that "static typing is communist bureaucracy". Some of the areas worth rebutting:

  • Static typing helps tools

    Yes, it does (although dynamic typing doesn't preclude tool support -- Smalltalk had the one of the first refactoring tools built in, and NetBeans is adding some pretty good support for JRuby). This is a problem that will be solved. However, I reverse this question: tools make static languages merely palatable. Iterating over collections in Java or C# is ridiculously painful. Using the each method with a closure in Ruby or Groovy is a joy. Tool support in languages cover up for deficiencies in languages and libraries. Graeme Rocher (creator of the Grails framework) stated this quite elegantly in the GroovyOne panel I was on, stating that you don't need as much tool support in well designed languages because they tend to be more consistent (what Martin Fowler calls "Humane Interfaces").

    Granted, tooling makes things easier: no question there. But, are you willing to give up the flexibility of a dynamically typed language just to get better tool support? In my aboutGroovy podcast, I said (and meant it) that I would rather write Groovy in VI than Java in IntelliJ (and I think my position on IntelliJ is pretty clear).

  • Static analysis tools are useful.

    Yes, indeed, anything you can find with an automated tool is low-hanging fruit. But, again, it's not a pure trade-off. You can do some of the same kinds of things in dynamic languages (although clearly not as much), but I would still rather have the flexibility of the dynamic language plus tests to verify my code. In the perfect world, I'd have the flexibility and power of the dynamic language and the tooling possible and available for static ones. This is one of the places that Groovy comes in handy: if your static analysis tool works off byte code (like FindBugs), you can run it on a Groovy codebase.

  • Static typing creates better contracts and calling semantics for libraries.

    Dynamic languages tend to eschew strong contracts like interfaces. What I've found it that it forces developers to create much better method and parameter names, because that becomes the primary way that you consume methods. When done right, this is an effective way to communicate. Granted, this is not universally the case even in the Ruby libraries, but removing props sometimes forces people to make better use of what's left. The best example is the town of Drachten, in the Netherlands, who removed all their traffic signs and lights, forcing people to interact in the intersections. It made their streets much safer because they removed signage.

Sure, in a perfect world, all the facilities that we lean on in statically typed languages would be available in dynamic languages. But they're not. And, having worked extensively in both static and dynamic languages, I prefer the flexibility and expressiveness of the dynamic languages currently extant to the static ones, even with all the tool support. Still, testing is the most important verifier of code. I think it is professionally irresponsible to write code that isn't tested. If you are going to write that level of testing anyway (especially if you do it test-first), a dynamic language allows you to get more done in less time with a lot less typing (a lot of which is required to make static typing work).

Tuesday, May 08, 2007

Static Typing is Communist Bureaucracy

In a conversation that came up at No Fluff, Just Stuff Denver this last weekend, a fundamental realization came to me. We were talking about dynamic languages like Ruby and Groovy, and a lot of the Java developers just couldn't get over the idea of dynamic typing. They feared that utter chaos would reign if we discard typing. First, I asked them about the significance of testing: "Do we all agree that testing is important?" to which they replied "Of course". And, in the course of the expert panel, we had discussed the appropriateness of code coverage, what it tells you, what it cannot tell you, etc. The general consensus was that code coverage is a Good Thing, and that we should all aspire to 100% as much as possible. After all, it's the tests that tell you if your code if correct or not.

Once we had agreed that testing + code coverage is a good thing, I realized: Static typing is Communist Bureaucracy. "To get bread, you must stand in the line over there, but first you must get a blue stamp on your booklet." If you have pervasive testing, static typing == more typing. The static typing is nothing but a requirement to type extraneous code to satisfy a compiler that isn't telling you anything interesting anymore.

I've said it before and I'll say it again: testing is the engineering rigor of software development. Testing destroys the need to static typing. Dynamic typing speeds up development because there is physically less to type. And testing verifies the correctness of your code, not just that it compiles.