I was reading Pat Eyler’s interview with the JRuby guys (that’s part 2, Part 1 is good too); and I had an idea about building IDEs for dynamic languages like Ruby.

IDEs do a bunch of nice things for you. A lot of people think the nicest is the refactoring support; check out Charles Nutter’s narrative in Part 1 about how it’s helped them in their work on JRuby.

The refactoring I use all the time is renaming, but there are lots more.

Find Usages · IDEs all have a “Find Usages” feature, which in itself is handy (“I’m thinking of nuking this stupid method, where is it used?”) but if you think closely, is also vital too pretty well every other refactoring operation. If you’re going to rename a method or a class or extract an interface, it’s absolutely vital that the IDE know all the places where that code is used; otherwise refactoring equals breakage.

Of course, the problem is harder with dynamic languages, because variables aren’t declared, and because you can screw around with the insides of classes at runtime, and for a bunch of other reasons that Thomas Enebo discusses in Part 2.

Thomas points out that, for refactoring, “open definitions and detecting callers, etc., will only be accurate if you are actually running the code. Which is something you really don’t want your IDE/editor to do”.

Well, I thought about that, and I realized that in fact, my IDE runs my code all the time. Another thing that IDEs make easy is unit testing, and competent programmers take advantage of this and are hitting the “run tests” button every time they change anything, just to make sure they haven’t broken anything.

Unit Testing and Learning · So I wonder about bolting a bunch of apparatus on the side of the unit-test runner. Because when the test is running, if the IDE can peek inside the run-time, it’ll be able to tell exactly which types get assigned to which variables and which method-calls go where.

So I’m thinking of the IDE doing this and then persisting the information, so that whenever you’ve got your cursor on a variable, it has a chance to look at the last test-run cookie crumbs and figure out what that variable contains. The effect would be, the better your unit-testing coverage, the smarter your IDE would be, and the more useful auto-complete would be, and the more refactoring would be possible. Is that a virtuous circle or what?



Contributions

Comment feed for ongoing:Comments feed

From: Clayton Wheeler (Sep 29 2006, at 23:48)

As long as you're instrumenting what goes on in the test run, you probably want to do some tracing to track your test coverage, too, and maybe color the untested bits of code amber so it's obvious what isn't getting exercised. Of course, I wonder if I couldn't just jam that into XEmacs...

[link]

From: Kim (Sep 30 2006, at 03:07)

Hi. The comment system doesn't work with Internet Explorer 6. The comments are displayed fine, but the link to the comment form doesn't display ("Please feel free to contribute a comment on this fragment.")

[link]

From: Arkaitz Bitorika (Sep 30 2006, at 11:39)

I had the same idea myself a while ago! The widespread use of test driven development with dynamic languages should allow really advanced IDE functionality without needing any explicit/static typing language. I can envision an environment that keeps test code constantly running in the background, providing information to the programmer for refactoring, profiling, etc. We have IDEs that auto-compile everything as soon as it's changed now, how long before IDEs auto-run test code and use runtime information in smart ways?

[link]

From: Reinier Zwitserloot (Sep 30 2006, at 16:22)

Retort: http://www.zwitserloot.com/2006/10/01/pythonruby-script-languages-nothing-more/

Summary: Are you nuts? Short of 100% code coverage any refactorings are always an unknown, and any bugs caused by it will by definition not be caught by any of your unit tests. Reaching 100% code coverage itself is waaaaaaaaaaaaaaaaaaaaaaaaaaaaay (I cannot stress enough by how many factors!) more work compared to the very mild job of adding types to all your declarations, something your IDE practically does for you, I might add. Not to mention the time it saves you on debugging.

I'm beginning to believe that python/ruby folk are so obsessed with unit tests exactly because they can't trust their compiler any farther than they can throw it, because it can't help but see your code as a pile of text instead of a structured set of links.

[link]

From: anonymous (Sep 30 2006, at 19:31)

I do "FindUsage" all the time without an IDE: I just delete the method and run javac. I *know* the compiler is going to get it right.

[link]

From: smalltalker (Oct 01 2006, at 03:37)

As with everything else: it's all be done before:

Smalltalk. Infact refactoring was pioneered (like many other things, oop, gui, unit testing, agile/xp ...) in Smalltalk!

Smalltalk has 30 years of experience making the Ruby object model fast.

[link]

From: Damien Pollet (Oct 01 2006, at 06:38)

There is a package an Smalltalk that does this kind of dynamic type guessing; you might want to have a look (in fact I should too :-] ). It's available at least in Squeak (http://www.squeak.org) and VisualWorks (http://www.cincomsmalltalk.com) and is named RoelTyper.

[link]

From: Isaac Gouy (Oct 01 2006, at 07:06)

Tim Bray wrote "If you’re going to rename a method or a class or extract an interface"

iirc the problem is rename method / safe-remove method. (Don't understand why rename class would be a problem.)

anonymous wrote "I just delete the method and run javac"

What if the method was also implemented in a superclass?

[link]

From: Cedric Beust (Oct 01 2006, at 11:00)

Smalltalker: The Smalltalk IDE never did "true" renaming, only a search/replace. It might have been fine 20 years ago when everything was simpler, it's not going to fly today.

Tim: As you probably already know, this approach is simply not realistic. If you have anything less than 100% coverage (which, I contend, is probably 100% of software projects out there), the refactoring remains unpredictible and is guaranteed to break your code. Who wants a refactoring IDE that "works most of the time"?

The bottom line is that IDE's for dynamic languages will *never* be able to perform certain refactorings, such as renaming. It's just part of the trade-off of using such languages (and one reason why I'm convinced that they are not suited for large-scale software development).

--

Cedric

[link]

From: Alan Green (Oct 01 2006, at 14:44)

I would have been disappointed not to have seen some Smalltalk person or another comment already. (S)he does have a good point, though, anyone setting off to provide IDE support for dynamic languages would do well to take a good look at the the various Smalltalk IDEs first, just to see what worked and what didn't work.

[link]

From: Charles Oliver Nutter (Oct 01 2006, at 14:58)

This is an interesting way of gathering information for refactoring. I'll break down a few possibilities and difficulties as I see them:

- We can gather information both from unit tests and from full-on runs if we run within an instrumented runtime. I don't think that would be difficult to do; in the JRuby project we've had multiple proposals and ideas on how to speed up dynamic invocation by gathering exactly this sort of information.

- By tracking actual types called at runtime we would avoid a much more expensive static analysis of the code. It would be possible to do an ahead-of-time analysis to track types as they move through a system, but it would be difficult to do accurately and quickly. The runtime-data approach may work better in some respects.

However...

- Knowing when to invalidate that data would require that a separate component be examining code as it is edited. For example, we would have to keep information gathered from each running file separate, so that if a file changed we could throw out that piece of information. That might require more work to thread together all those separate pieces of information into something useful for refactoring, or to regenerate that "something useful" after every change, given what we still know.

- The longer you go without running, the less accurate the gathered information will be. Obviously we all know you should run unit tests frequently, but if you're working on a particularly difficult change, test runs may be more infrequent.

- We would want some way for the refactoring to feed back into the gathered information. If we did a "rename method" that affected 70% of our files, we would not want to have to throw out all information gathered.

I think runtime information will be a major component to any refactoring IDE, but I'm still convinced we'll have to combine dynamic runtime information with static analysis to have the best effect.

[link]

From: Issac Gouy (Oct 01 2006, at 15:02)

Cedric Beust wrote 'Who wants a refactoring IDE that "works most of the time"?'

Does method-rename refactoring in Java IDEs work when methods are invoked with the reflection API? Does it work across all those webservices XML config files?

(A method-rename refactoring that "works most of the time" is actually really useful.)

iirc "certain refactorings" is just two - method rename and safe method remove.

The strange thing about using type recovery from unit test runs to provide type information for refactoring - is that we could just do the refactoring and then see which unit tests fail.

[link]

From: Jeff Rose (Oct 02 2006, at 02:15)

Here's another idea... What if refactorings generated code which would wrap your current project AOP style. Then the next time you run a test or execute the program, whenever a refactored method was called it would hit the wrapper, check the type, and then decide whether to call the newly refactored name or use the old name because the type doesn't match the refactored class. Once the decision has been made it can get rid of itself both in the running code as well as in the back-end of the IDE.

Just a thought.

[link]

From: murphee (Werner Schuster) (Oct 02 2006, at 03:11)

How Dynamic Refactoring Works in Smalltalk (and Ruby?):

http://jroller.com/page/murphee/20051209

This also contains a long list of cases where all Java Refactoring tools fail, eg. components tied together with XML, scripted Java classes, uses of Reflection, JSP, etc etc. This means, that you'll still need high test coverage, otherwise you won't catch all the problems a simple rename brings. Mind you: in Ruby, many of these approaches are done in Ruby, instead of having to step outside the language (as in Java with XML and friends).

The Eclipse RDT (http://rubyeclipse.sourceforge.net/) guys have done work on Type Inference (sponsored by the Google Summer of Code):

http://soc.jayunit.net/

They're busy adding that to one of the next releases.

[link]

From: Stefan Arentz (Oct 02 2006, at 03:16)

Issac: "Does method-rename refactoring in Java IDEs work when methods are invoked with the reflection API? Does it work across all those webservices XML config files?"

No of course not. Well, partly. When I rename a class in IntelliJ IDEA that is also referenced by name in a string literal or in some XML file it does actually know to refactor those too. That is something I do all the time. Not sure about method names but there will always be things that cannot be refactored because they are too obfscated in the code. But in case you are doing reflection stuff like that in your project then you probably also know that you should check those cases manually after refactoring someting that touches that code.

S

[link]

From: Jason Zaugg (Oct 02 2006, at 05:00)

A full suite of unit tests that achieve 100% code coverage may not yield useful information about call heirachy.

In unit tests that I write collaborators are mocked out in order to test the class in isolation. (This differs from the definition of unit test popularised by Rails)

Integration and Acceptance tests that test the interaction between classes would provide useful data.

[link]

From: Isaac Gouy (Oct 02 2006, at 07:44)

Stefan Arentz wrote: "...there will always be things that cannot be refactored because they are too obfscated in the code."

Indeed, and that means we have Cedric Beust's 'refactoring IDE that "works most of the time"' - and it's actually really useful.

(It's still good to hear that IntelliJ continue to extend the scope of their refactorings.)

[link]

From: Justin Rudd (Oct 02 2006, at 23:19)

Isaac Gouy asks - "Does method-rename refactoring in Java IDEs work when methods are invoked with the reflection API? Does it work across all those webservices XML config files?"

In Eclipse, you can tell it to search other files and string constants. It isn't the default though. So you would miss them unless you explicity check the option to do it.

Personally, I'd prefer "good enough" to nothing at all :)

[link]

From: John Stracke (Oct 03 2006, at 11:06)

<blockquote><p>[...] "if you are actually running the code. Which is something you really don’t want your IDE/editor to do”.</p><p>Well, I thought about that, and I realized that in fact, my IDE runs my code all the time.</p></blockquote>

That's kind of a scary thought to me. When I tried out Smalltalk, about a year and a half ago, the biggest failing that kept me from sticking with it was that the IDE ran in the same interpreter as my own code, which meant that I was perfectly capable of writing code that would trash the IDE. The second time the VM crashed, and I lost a large chunk of work, I gave up on Smalltalk.

[link]

From: Isaac Gouy (Oct 04 2006, at 11:00)

John Stracke wrote "The second time the VM crashed, and I lost a large chunk of work, I gave up on Smalltalk."

No John you didn't loose a large chunk of work, you just didn't know how to recover your work. There are online Smalltalk communities and they do help newbies who make the effort to ask ;-)

1) The most basic Smalltalk implementations provide these 4 pieces - the VM, the Image .im, the Sources .sou, and the change log .cha

The change log records changes you make while you're working with Smalltalk, in a format that can be filed back into an image to recover your work:

<methods>

<class-id>Shootout.Tests class</class-id> <category>benchmarking</category>

<body package="ComputerLanguageShootout" selector="takfp">takfp

| n |

n := CEnvironment argv first asNumber.

^(((n * 3.0) takfp: (n * 2.0) z: (n * 1.0)) asStringWith: 1) withNl</body>

</methods>

2) File\Save Image can be used at any time to create a snapshot of the current state of your work

3) Back in the late '80s large scale Smalltalk development moved to fine-grained multi-user version control - every time someone accepted a method change, a new edition of that method was created in the shared code repository (and could then be made public or not).

Did you really think that Wall Street investment banks would have used a technology as fragile as you describe?

[link]

author · Dad
colophon · rights
picture of the day
September 29, 2006
· Technology (90 fragments)
· · Coding (98 more)
· · Dynamic Languages (45 more)

By .

The opinions expressed here
are my own, and no other party
necessarily agrees with them.

A full disclosure of my
professional interests is
on the author page.

I’m on Mastodon!