A comment to my previous "oh my god" post just came through. It is the typical "static typing is good because it enables good tool support like refactoring and finding methods". Mmmm.
First let me agree that tool support for Python and Ruby truly suck. They do. I still love the languages and would use them in a heartbeat before any static language. But the tool support sucks. But not because the languages are "dynamic".
To make my point a bit more concrete, here is a snapshot of a Smalltalk image I just created...
The aqua colored window is a "method finder". I entered the text "
#(1, 2, 3, 4). 2. true", which is an array of numbers, the number 2, and the boolean true.
The method finder then found five methods that when applied to the array and the number, return true. It found those for me by considering potential candidate messages and executing the method code for me. The matches are listed in that window's pane just below the text I entered.
I then selected from that list the method "
windowReqNewLabel:" and the method finder brought up the green window which is a system browser. It set the browser to display the code for that selected method. (By the way the browser has a complete kick-ass refactoring engine available. But that's for another post maybe.)
In the system browser for that method I clicked on "senders". This brought up a menu consisting of that method's selector as well as all the other message selectors that are used in that method. I chose that method's selector because I hypothetically want to know all the places this application sends that message. (Otherwise I could find all the senders of messages that this method also sends.)
Doing so brought up the front, light blue, window. This is the "Senders of windowReqNewLabel:" window. This window lists the four places that message is sent in this application. You can see I have selected the third occurrence, and the code for that sender is displayed in the bottom pane of that window.
As you can see, tools for dynamic languages only suck when they have not been implemented. On the other hand everything you see in this screenshot has been in Smalltalk for decades.
Decades of better tools than you can find for Java, C, Scala, you name your favorite statically typed language.
I am sick and tired of people whining about how dynamic languages cannot support useful tools. They can, and they are *better* than yours. This example is the tip of an iceberg I just captured in a matter of seconds. That first method finder "search by example" completes in a split second.
Can your static tools do that? Maybe, which is why I won't go around claiming that they can't, because I am ignorant of that, just as you are ignorant of what dynamic language tools can do.
An anonymous commentator points out that one of us is being obtuse. How can a dynamic language possibly refactor the name of a message when there are three implementations of that method but only one of them should be renamed.
I will quote from the documentation for the very first usable refactoring tool ever. (This very first one ever was written in Smalltalk and is the one that still is used by Smalltalkers.) The quote...
One of the key features of Smalltalk that allows us to perform the sorts of analyses and source code transformations necessary for refactoring is that it has reflective facilities, that is, the ability for the language to examine and modify its own structures. For instance, the Refactoring Browser examines the context stacks of executing programs to determine runtime properties such as callers of a particular method or cardinality relationships between components and aggregates. Refactoring can be performed in the absence of reflective facilities, but then requires a separate, metalanguage that can be used to manipulate the original program. Having a single language simplifies the process...Except that good test suites are never a major drawback. They are actually the only thing that can save your application over time, dynamic or static.
Some preconditions of refactorings are not simple to compute from the static program text. With Smalltalk's dynamic typing, determining the type of a particular variable can be extremely difficult and time consuming. Moreover, there are analyses that defy static analysis in any language, such as cardinality relationships between objects.
Since, performing these types of analysis statically is difficult or impossible, the Refactoring Browser computes some of the preconditions by performing the analysis dynamically rather than statically. By observing the actual values that occur, the Refactoring Browser can perform correct refactorings. As an example, consider the rename method refactoring. To correctly rename a method, all calls to that method must be renamed. This is difficult in an environment that uses polymorphism to the extent that Smalltalk does. Smalltalk also allows dynamically created messages to be sent via the perform: message. If an application uses this approach, any automatic renaming process has the potential of failure. Under these conditions, guaranteeing the safety of a rename is impossible.
The Refactoring Browser uses method wrappers to collect runtime information. These wrappers are activated when the wrapped method is called and when it returns. The wrapper can execute an arbitrary block of Smalltalk code. To perform the rename method refactoring dynamically, the Refactoring Browser renames the initial method and then puts a method wrapper on the original method. As the program runs, the wrapper detects sites that call the original method. Whenever a call to the old method is detected, the method wrapper suspends execution of the program, goes up the call stack to the sender and changes the source code to refer to the new, renamed method. Therefore, as the program is exercised, it converges towards a correctly refactored program.
The major drawback to this type of refactoring is that the refactoring is only as good as your test suite.
And so you see good refactoring tools, good (dynamic) languages, and other good things go hand-in-hand with good tests and good test coverage. Working incrementally helps you make only small changes at a time, testing well points out just the few things you just broke, good coverage makes sure you find everything, and refactoring tools introspecting the code that your good tests cover help automate those changes and bring them up to a higher level of expression for you.
No single tool or practice is in itself sufficient. They work together for the good of the whole. Should I assume your refactoring tool for your static language works so well as this? I believe method wrappers, etc. take a fair bit more machinery and end up not so expressive in static languages. At best they require more code than with dynamic languages.
You do not have to compromise your very simple language just so your tool can work. Dynamic languages and good tools work for you. Static declarations -- they appear to work for you until they don't. They're not a compromise, they are a bloody surrender, somewhat quoting Guy Steele from some long ago Lisp memo I don't fully recall.
Oh my god here I go again. I know you love your static languages and type theories. Good on you. I don't.