refactoring?

40 views
Skip to first unread message

Raoul Duke

unread,
Jul 22, 2008, 9:50:51 PM7/22/08
to clo...@googlegroups.com
i am admittedly normally a static typing bigot, but things like Scala
are kinda scaring me, and Clojure's concurrency support is
stupendously interesting. so, i'm asking this not to be snide but to
honestly learn more: how might refactoring be safely done in Clojure?
is the approach to mainly leverage unit tests to detect errors in
refactoring? are there ways to do automatic refactoring (i need to try
out enclojure and see if it has refactoring tools).

many thanks.

Chas Emerick

unread,
Jul 23, 2008, 7:59:09 AM7/23/08
to Clojure
Raoul,

Refactoring in "modern" dynamic languages is something of an oxymoron
in most contexts. That's slowly changing, as we see progress being
made with refactoring Ruby in the NetBeans environment (with something
similar perhaps in the pipeline there for Python, too). Of course,
there was bicyclerepairman for python some years ago, but I think that
could safely be described as "experimental".

Dynamic languages that operate within an "image" (like Smalltalk, and
many Common Lisps) often have outstanding refactoring features almost
baked into the environment -- because there is complete visibility of
all of the code in a particular system/project, it's conceptually
trivial for the environment to offer all sorts of refactoring
functionality.

I suspect we may eventually see the same kind of functionality
available for Clojure. It's pretty early on in the language's
(public) development right now though, so tooling is very much in its
infancy.

That being said, to answer your question, no, Clojure doesn't
currently have any refactoring tools (or, to be precise, no Clojure
editing environments yet provide refactoring tools, including
Enclojure). That being said, I would offer these three points to
allay your concern, since you self-identify as generally preferring
static typing:

(1) Refactoring is generally less necessary when using a dynamic
language (like Python, Ruby, or yes, Clojure) than it is in a
statically-typed language. In the latter, refactoring is often
required in order to get code to where it needs to be in order to fit
into the object system so it can be utilized as necessary by other
code (this is where the push up/down, encapsulation, anonymous->inner
class refactorings are mostly coming from). Those sorts of concerns
almost never come up in dynamic languages (at least in my experience)
-- there are typically few restrictions being placed on the
accessibility of code, so it's up to each programmer to use her best
judgment and follow best practices, respect "private" labellings put
in place by library authors, etc. Beyond that, less code is required
to do things in Clojure than in Java certainly, and likely even Scala
(consider Rich Hickey's ants example, which bolts together a robustly-
concurrent UI simulation in ~300 lines!). Assuming that the value of
good refactoring tools goes up as your codebase gets larger, and the
inverse is true, then not having refactoring tools on day one might be
a reasonable tradeoff.

(2) When you do need to refactor something (in Clojure, or Python, or
Ruby, etc.), you generally need to piece through it one bit at a
time. The most common refactoring that you'll want to perform is a
simple renaming (since, as I said in (1), most other refactoring use-
cases are rarely if ever applicable). In this case, a good editor or
IDE environment that allows you to safely preview a find & replace
operation across an entire project is helpful and recommended. If you
think using a crude F&R to do a rename refactoring sounds like
potentially-dangerous drudgery, I'd absolutely agree. It's a lot more
work than in a statically-typed language, and in many dynamic
languages (like Python and Ruby) unit tests and `svn revert` are all
that keep you from oblivion. Clojure helps beyond that, though,
because...

(3) While Clojure is dynamic, it's also *compiled*. That means that
it will throw an exception if a symbol is not in scope where you are
using it, so if you perform a renaming "refactor" using an F&R
approach on a Clojure codebase, simply loading up all of the files in
that codebase will immediately reveal any serious errors. We have an
ant process that does this (and checks for any reflection warnings to
boot, so that we can ensure that we're adding type hints as
necessary), so we have a pretty solid safety net. This is a huge
advantage for Clojure compared to its other dynamic friends, which
generally happily read in code that reference nonexistent "symbols",
and don't throw an error until the app has been running for three
weeks, at which point the faulty code path is evaluated. (It's at
this point where the TDD folks will say to me, "You're Doing It
Wrong". :-)

I hope that seems like a balanced, albeit opinionated perspective.

Cheers,

- Chas

Rich Hickey

unread,
Jul 23, 2008, 9:22:53 AM7/23/08
to Clojure
To which I'd add - Yes, don't presume the story will be the same with
Clojure as it is for other dynamic languages.

Many refactorings in (dynamic or static) OO languages have to do with
changing the hierarchy or moving things between classes. Since Clojure
doesn't partition its functions into classes, these go away.

The number one refactoring is rename. As Chas said, the Clojure
editing environments don't currently support it, but I think Clojure
is well-positioned for rename refactoring, especially when compared to
Python and Ruby. There, given a rename refactoring of Foo.bar to
Foo.baz, it can be very difficult to determine statically whether some
call of the form x.bar should be renamed, since the type of x is often
unknown.

By contrast, a rename refactoring of foo/bar to foo/baz in Clojure
should be straightforward, since every use of bar can be resolved
statically to either a local name or a name in a specific namespace.
Thus, only those bars that resolve to foo/bar will be renamed.

In short, namespaces make for better namespaces than do classes in
dynamically typed languages.

I look forward to good rename refactoring for Clojure. It could even
(preferably?) be implemented a la carte, as an independent process
invokable by all tools.

Volunteers?

Rich

rob....@gmail.com

unread,
Jul 23, 2008, 11:43:01 AM7/23/08
to Clojure
"Rename" is probably the simplest thing that could justify being
called a refactoring, but I think most people would consider "extract
function" to be the fundamental building block of the refactoring
world.

What do people think of the possibility of implementing extract
function, as part of an Clojure IDE/Editor? Do macros remove the
ability to deterministically perform an extraction? (I'm not saying
they do or don't. I'm not qualified to say.)

Rob Lally.
Reply all
Reply to author
Forward
0 new messages