Bruce Tate has another articel up on the "Crossing Borders" site hosted by IBM. In it, he discusses the advantages of dynamic typing, and contrasts it with static systems. Part way through the article is this description of Smalltalk, which is a great summary:
The whole Smalltalk language is built on the premise of delayed binding. Smalltalk developers build onto a continuously running application called the image. Because the image is always running, any addition, deletion, or update of a method in any class occurs at run time. Delayed binding lets Smalltalk applications keep running throughout the development cycle.
This is the thing about Smalltalk that differentiates it from other languages. It's always runtime in Smalltalk - the divide between development and deployment is very, very blurry. In fact, my test server for this blog is usually just sitting on my old Linux box, waiting for me to do something with it. When I have new code, I export it, toss it at the server, and load it into the running server.
Those changes often involve shape changes to live objects - including ones that are being used by requests to the server at the time of the code change. I don't shut the server down; heck, I don't even turn the listener off. I just use an administrative interface I set up to tell the server to look in the update directory for new code - and it loads it. This all works because of the lackof a full divide between development time and runtime in Smalltalk.
That leads right into something Bruce says further down in his article:
They [ed: dynamic languages] provide techniques that the Java language doesn't, such as overriding the behavior that happens when a method is missing. Remember, the Java language requires methods to exist for compile-time binding. Other languages allow open classes -- classes that can change based on a developer's needs. When you look long and hard at the evolution of frameworks, you find an increasing need for delayed binding, leading to increasingly unnatural bolt-ons in the Java language that complicate and obfuscate the language. Meanwhile, other languages sit ready and waiting for us to build just the sorts of frameworks that can lead to radically higher levels of abstraction, and much better productivity. The benefit to you is clear: you have a language that's more expressive and more productive.
The runtime/development time divide is something that grew out of a world of limited computing resources: memory and diskspace used to be expensive. At this point, they're cheap (and only getting cheaper. Inertia is the main force that keeps that divide around. having dynamic binding adds other possibilities as well:
Message passing takes on another dimension when you throw method_missing into the mix. Remember, this capability is open to dynamic languages, but completely closed to languages that bind at compile time to a type. The benefit of the early binding -- enforcing that the method must exist -- also turns out to be a core weakness. Ruby lets you override the method_missing behavior to invoke behaviors for methods that might not exist at run time. Active Record associates a class with a table and dynamically adds an attribute to each class for every column in the database. Active Record also automatically adds finders for each column, or combination of columns! For example, for a Person class mapped to a people database table having first_name and last_name columns, person.find_by_first_name_and_last_name is a legal Active Record statement, though no such method exists. Active Record simply overrides method_missing and parses the method name at run time to determine whether the method name is valid. The result is an extremely productive framework for wrapping database tables -- one greatly simplified through the power of late binding. But so far, I've only explored the invocation side. It's time to push forward into adding behavior.
One of the nicest things about having easily modified MNU (method missing) behavior is proxies. In Smalltalk, setting up proxy behavior (for lazy fetching from a database, or for over the wire method invocation) is trivial - the proxy overrides the default MNU behavior, and does the appropriate action (fetches the db data, sends an RPC message, what have you). In Distributed Smalltalk, a CORBA implementation for Cincom Smalltalk, MNUs raised remotely result in bringing up a debugger locally - the remote side handles the exception the same way it normally would, but with the debugger coming up locally. That debugger is a full Smalltalk debugger as well - it's not the "forensic pathologist" kind of debugger that you get in more traditional environments.
The funny thing is, the Java world is headed this way, but at the cost of ever spiralling levels of complexity. Trying to add dynamism to the static world of Java is a little like handing a bicycle to a fish:
The Java community's obsession with static type checking is curious because Java developers are now spending an ever-increasing amount of energy looking for ways to delay binding. Sometimes, the approaches are successful. Frameworks such as Spring exist primarily to delay binding, which loosens the coupling between client and service. Aspect-oriented programming allows delayed binding by providing services that extend a class beyond its current capabilities. Frameworks like Hibernate delay binding, adding persistence capabilities to plain, ordinary Java objects (POJOs) at run time through reflection, proxies, and other tools. Popular books teach developers how to program with POJOs, often moving beyond reflection with increasingly complex techniques essentially used to open up a class and delay binding, effectively sidestepping static typing.
When you start down that path, you might want to consider using a language and environment that were developed with that stuff in mind - right tool for the job and all that. Put another way - come on in, the water is fine.
dynamic typing, smalltalk, ruby, java