A New JRuby Interview and More
Okay, non-interview stuff first. I've recently picked up several 'shortcuts' from O'Reilly and Addison-Weseley. I love this format. For about 10 bucks, you can get a PDF only copy of a 50-100 page "book". The shortcuts (so far at least) have been very focused, which allows them to cover a reasonable topic in sufficient depth without creating a monstrous 600+ page tome. The shortcuts I've looked at so far have been timely, useful, and a great value. October marks the beginning of Apress' push into Ruby and Ruby on Rails. Apress also has Practical OCaml coming out soon to help soothe your inner functional programmer. Now, on to interview news!
Since the news about Sun and JRuby broke at the beginning of the month, I've tried to stay close to the JRuby team to see what's going on. Our discussion has turned into another interview. (The first is here, at the O'Reilly Ruby Blog.)
Okay Charles, since you brought up refactoring tools — you and Thomas, are supposed to be looking at programmers tools (which most people read as NetBeans). What do you think has been holding back refactoring tools for Ruby? More importantly, what can/will you be doing about it? (Ola, I'd love to hear your thoughts on this too.)
Charles: Refactoring a dynamic-typed language is inherently hard for reasons that are pretty easy to understand. If you can't look at a piece of code and know exactly what type a given variable is, an IDE won't be able to either. Even if you can do so with some difficulty, IDEs are out of luck. This makes the most interesting and useful refactorings monumentally more difficult to implement. If you can't know what type a variable is or what type of object it contains, you can't tell until runtime what operations it actually supports. Ruby's fluid typing and support for metaprogramming complicates things further: sometimes even the list of operations on a known type will change during a given run. The more dynamic and fluid the type system, the more difficult it is to write ahead-of-time refactoring and analysis tools. There's research going on and creative potential solutions in the making, but there's still a long way to go.
I like to think of dynlang refactoring as being in the same class of problems as AI for the game of Go. In Go, the spatial relationships on the board and the broad array of possibilities make long-term strategy much more difficult. For a computer, this causes no end of problems. In general, the best game AIs are the ones that can brute-force a problem by trying more combinations than a human. In Go, where there's such variation, that method quickly becomes intractable. Where humans can infer future results from the current position and get a "feel" for where a game is progressing, computers can only make use of hard, tangible information about the game. When we develop in dynamic languages, we're much better than the computer at feeling our way through the code, inferring types based on parameter names or methods we've seen called, or reading backwards to get a more concrete feel for incoming object types. Perhaps it's why dynlang programming starts to feel so right...it's putting trust and control back in our hands.
You might say that dyntyped languages trust the programmer to do the right thing where static typed languages /force/ the programmer to do the right thing. That trust opens up a world of possibilities, but it also takes some responsibility out of the computer's hands--namely, responsibility for exactly that information that's needed to refactor.
Thomas: I have not had much personal experience with the refactoring issues of Ruby so my thoughts should be taken with a grain of salt. For a more weighty discussion I recommend talking to Jason Morrison or Mirko Stocker. They both have been working towards type-inference/refactoring.
One philosophical barrier I see is that computer scientists like 100% solutions. Refactoring in Ruby is probably always going to be a 90% solution. It's 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. I think being only a 90% problem makes it a less desirable or more frustrating problem to tackle.
We have worked well with the RDT team in the past and we hope to continue in that vein. In fact, we really want to help support all IDE/Tools makers in any way that makes sense. Originally, for RDT we added better positioning information in our AST, so that constructs could be highlighted/folded. Next we plan on merging the refactoring changes that Mirko, Thomas, and Lukas did during this summer.
Ola: Refactoring is a great subject, and I really believe Ruby could do incredibly much. I believe this since Ruby isn't really more dynamic than Smalltalk, and the best Refactoring tools have traditionally been developed for Smalltalk. The Refactoring browser was a great tool that I would really see someone port to Ruby. The big difference between Smalltalk and Ruby lies in the fact that Ruby code is file-based, while Smalltalk always has been memory-based. This is a caveat, but not a big one. If I had the time I would love to write an implementation of it for Ruby. (And of course, this would be for Emacs in the first place. =)
Charles: Ola's point about Smalltalk has a lot of weight as well. Smalltalk's refactoring browsers are fully-capable refactoring environments, and they did refactoring before it was even fashionable to do so. However they line up better with Ruby's interactive shell IRB. IRB is an essential tool for any Ruby programmer in the same way that Smalltalk's browser was an essential tool for Smalltalkers. IRB provides a "live" environment where you can query objects, call methods, and inspect types. This could certainly be extended out to an IDE scale, but there's a new class of problems when you go that direction. You need to ensure that you're not causing side-effects as you traverse code. You need to know when metaprogramming bits are coming into play. Perhaps most importantly, you need to be able to roundtrip an in-memory picture of the system back to code. These features are all absent from Ruby and IRB today, so they represent challenges to building a full-featured refactoring Ruby browser. Even then, Ruby's ability to rewrite itself through a wide variety of eval calls makes refactoring with 100% confidence nearly impossible. I believe that some combination of static analysis and online refactoring will be necessary to sufficiently solve the refactoring question.