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.
Begin Update
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.
End Update
26 comments:
Patrick, you've squeezed out another stinker: this isn't very convincing :/
I don't see how you can properly perform refactoring, or many forms of static analysis.
For one example, if you have
class C:
def foo(self,x):
# oh whatever
class B:
def foo(self,x):
# oh whatever
def func(c):
c.foo(5)
and you perform a method rename on C.foo to be C.bar, how do you know whether to rename B.foo or the reference to foo in func?
answer: you do not
"Can your static tools do that?"
If the static language was image based, probably :-)
"Decades of better tools than you can find for Java, C, Scala, you name your favorite statically typed language."
That's true for people burying their head in the sand. Eclipse's tool support/refactoring support blows the Squeak stuff out of the water. I say this as a diehard Smalltalk (specifically Squeak) fan. Spend some quality time in Squeak, then spend some quality time in Eclipse. Eclipse has much more functionality, and doesn't find senders/receivers that don't apply because it has the extra type information.
As for the emotional stuff, come on! None of this stuff really matters all that much. Just enjoy using what you use!
Right on, but you don't even have the good stuff loaded, try my image...
http://onsmalltalk.com/my-squeak-image/
It's a fully loaded developer image with all the goodies like code completion, colored code, nicer windowing system and lots of good libraries loaded.
Squeak rocks, blog some more about it, I love peoples reactions to it.
Good thing I don't hope to convince you. But see the update to the post if you care.
"answer: you do not"
Only if your tools suck. Should we assume tools have to suck? Mine don't.
"That's true for people burying their head in the sand. Eclipse's tool support/refactoring support blows the Squeak stuff out of the water."
Eclipse and I have agreed to leave each other alone. Nice to see it has good things and works for you though. I have no problem with that.
"Right on, but you don't even have the good stuff loaded, try my image..."
I do have color pretty printing. I'll try the rest too. Thanks
"Eclipse has much more functionality, and doesn't find senders/receivers that don't apply because it has the extra type information."
Well, one: have you seen the update to my post. The applicable methods problem is not a problem.
And two: I hate the Eclipse experience. Just not as pleasing as the Smalltalk UI. For me. YMMV.
Then three: using Eclipse means using a language that Eclipse supports. I'm using Smalltalk in this example, and it makes no sense to use a Java-based IDE for a Smalltalk system.
Even if that Java IDE were pleasing to me, which Eclipse is not.
"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."
Sounds great to me for both static and dynamically typed languages/environments! One of the benefits of a statically-typed language is that the compiler handles a whole slew of unit tests that you don't even have to write.
thanks for responding to my example in your update :)
> The major drawback to this type of refactoring is that the refactoring is only as good as your test suite.
this is an interesting drawback. Many people have observed how static typing is really a set of automated tests expressed in a declarative way.
This result from the smalltalk experiment confirms this from another angle.
Relying on scraping information from program execution is a very flimsy way of performing program analysis? For performance (JVM run-time optimization) its ok - you want to optimize for the particular program run, but you don't want to possibly make your program incorrect.
You seem to think that the problem is that the tools here 'suck' rather than it being a property of the underlying problem domain - static analysis of dynamic languages.
Btw, any of the guys who wrote the Smalltalk tools you are clinging are the architects of Eclipse:
http://www.eclipsezone.com/articles/beaton-interview/
e.g. this paragraph:
There seems to be a lot of history between Smalltalk and Eclipse though.
There sure is. A good chunk (probably most) of the original Eclipse developers are former Smalltalkers. In fact, many of them are from the same group that created IBM Smalltalk. John Duimovich, lead of the Eclipse Tools PMC, was the lead designer and implementor for the virtual machine under IBM Smalltalk. Steve Northover, the principle architect behind SWT is the same guy who brought us Smalltalk's Common Widgets which provide similar functionality. Heck, even our own Mike Milinkovich was one of the original developers of the Envy/Smalltalk product which provided source code management for several Smalltalk implementations.
Let me get this right: imagine I wrote this class "CurrencyValue" with a method "add: aCurrencyValue". I notice that the method should rather be called "plus: aCurrencyValue" because it's functional, not mutating.
So I say "rename add to plus". The documentation tells me that the browser will happily rename Collection#add:, too, because it's using great reflection tricks.
Is that what you're saying?
But in general, unless people look each other over the shoulder at work, these debates will go on and on and on...
What I just gathered from that is that it really is a nightmare to implement the most bsaic of refactorings in a dynamic language, and that smalltalk has done it with brute force requiring the entire programme to be executed. That is an inelegant solution due to lack of type information.
Fundementally it is harder, your post has shown how much harder rather than proving the opposite. That doesn't mean its impossible, it simply means the tools and requirements to make them work are going to take longer.
An other thing about Eclipse (or IDEA for that matter which IMHO is superior) refactoring support is that preserves the formatting of methods. If you do a refactoring in Smalltalk with RB it will always pretty print all the changed methods.
"Many people have observed how static typing is really a set of automated tests expressed in a declarative way."
Go with it, if it works for you.
"This result from the smalltalk experiment..."
Experiment? It's been working very well for a long, long time.
"Heck, even our own Mike Milinkovich was one of the original developers of the Envy/Smalltalk product which provided source code management for several Smalltalk implementations."
Hey, if you all prefer Java and Eclipse now to Smalltalk, then I won't hold that against you. Your still good guys.
"Is that what you're saying?"
There are situations where the refactoring engine cannot tell statically. There are situations where it can. The runtime capability helps with the former. I've not found this to be a problem in practice at all.
"Fundamentally it is harder, your post has shown how much harder rather than proving the opposite."
Perhaps harder for the tool implementor. Different, but I don't know if it is "harder". On balance, the parser, etc. for Smalltalk is a heck of a lot easier.
But that work has been done already by the tool implementor.
On the other hand static typing gets in the way of application developers day after day.
I prefer the dynamic approach. If you don't, oh well.
"Relying on scraping information from program execution is a very flimsy way of performing program analysis?"
Scraping?
Actually runtime wrapping in Smalltalk, Lisp, and other dynamic languages can be pretty useful for a lot of things. Sorry your language makes that difficult.
What happens in Smalltalk projects where you grow larger? Where you want to divide a system up into components, and some of those components don't exist yet? How are interfaces between library and user code specified? Can such interfaces be browsed in the same way if the code can't be called?
Patrick, I like the Smalltalk way too but I just have to say that Squeak is plain ugly, and the image is filled with crap (eToys, etc ...) you don't want.
"Patrick, I like the Smalltalk way too but I just have to say that Squeak is plain ugly, and the image is filled with crap (eToys, etc ...) you don't want."
Smalltalk images are like Linux distributions in this sense: people can make all kinds of them.
And so there are small ones, there are ones with different look-and-feels, there are developer-oriented ones, etc.
One posted previously in this comment thread may suit you...
http://onsmalltalk.com/my-squeak-image/
"Smalltalk images are like Linux distributions in this sense: people can make all kinds of them.
And so there are small ones, there are ones with different look-and-feels, there are developer-oriented ones, etc"
I would not compare the Squeak smalltalk image with a linux distro because there are a lot of good looking big and small linux distro's but there is no good looking, user friendly lean Squeak image. Apparently its too hard because everything in the standard image is too intertwined. There seem to be some initiatives to make Squeak look better but nothing so far has impressed me ... and I have already had a look at Leon's image and although it does look slightly better than the standard image (makes you wonder why they don't try anything on that default image) but it doesn't address any usability issues.
One "problem" with this kind of refactoring in dynamic languages is that it requires a full integration test suite - where every possible interaction between the actual concrete objects is tested. This negates the use of mock based unit-tests.
In a static language you can instead rely more on unit-tests using mocks (avoiding requiring integration tests for every permutation of concrete collaborators), as the static typing will ensure the interaction will still be valid with any concrete object of the same type as the mock. And obviously refactoring still works.
That's not to say static is better - it just has slightly less overhead in terms of the tests you need (at the expense of being constrained by the type system as some would argue).
"it requires a full integration test suite - where every possible interaction between the actual concrete objects is tested"
I dunno Chris. Are you writing this because you have first-hand knowledge of repeated, nasty problems due to refactoring incredibly general message selectors in large, dynamic-language systems? Or are you just reaching for any argument?
I cannot say I've ever pushed the boundaries on refactoring something like #add: to something like #plus: in a really large, production system. In fact I can tell you for sure, I have *never* done that. It simply has not come up for me. And I have been programming in Lisp since the early 1980s and in Smalltalk since 1987, among other significant experiences.
Am I worried? Not really, until you come up with something more realistic. Usually renaming message selectors are fairly anachronistic within a specific system, for a specific reason, and I can never remember having any significant problems. Never. That's just my experience. But if you are worried, then stay far, far away from dynamic languages, and I am sure you will continue to be as happy as ever.
"That's not to say static is better - it just has slightly less overhead in terms of the tests you need (at the expense of being constrained by the type system as some would argue)."
I would argue with the significance of that "slightly less" measurement. I've never found it to be significant, and I have written thousands and thousands of tests in both static and dynamic languages of various stripes. You?
Hey, like I said, if my argument does not float your boat, what the heck do I care? Float it somewhere else.
Post a Comment