Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

What does Emacs/SLIME support for refactoring?

490 views
Skip to first unread message

Slobodan Blazeski

unread,
Mar 13, 2010, 9:55:50 AM3/13/10
to
I'm writing a lot of CLOS code and with that the usual problem of
renaming arrives so I'm interested what kind of functionality does
Slime/Emacs has for refactoring common lisp code? Is there anything
like Eclipse Refactoring functionality?

thanks
Slobodan

Pascal Costanza

unread,
Mar 13, 2010, 10:12:12 AM3/13/10
to

As far as I know, not a lot has been done with regard to refactoring for
Lisp dialects. A reason may be that the regular structure of source code
in Lisp already makes it very easy to move code around (in advanced text
editors) that the added value you could get from a refactoring tool is
not that big anymore.

So, what problem are you actually confronted with?


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/

Slobodan Blazeski

unread,
Mar 13, 2010, 10:19:41 AM3/13/10
to
On Mar 13, 4:12 pm, Pascal Costanza <p...@p-cos.net> wrote:
> On 13/03/2010 15:55, Slobodan Blazeski wrote:
>
> > I'm writing a lot of CLOS code and with that the usual problem of
> > renaming arrives so I'm interested what kind of functionality does
> > Slime/Emacs has for refactoring common lisp code? Is there anything
> > like Eclipse Refactoring functionality?
>
> As far as I know, not a lot has been done with regard to refactoring for
> Lisp dialects. A reason may be that the regular structure of source code
> in Lisp already makes it very easy to move code around (in advanced text
> editors) that the added value you could get from a refactoring tool is
> not that big anymore.
>
> So, what problem are you actually confronted with?
I currently need the renaming of classes the most.
(defclass foo ()
((name :accessor foo-name )
(bar :accessor foo-bar ...)
; in some files far far away
(make-instance 'foo ...

(foo-bar ...)

(mapcar #'foo-name ...
I would like to rename foo to baz
(defclass baz ()
((name :accessor baz-name )
(bar :accessor baz-bar ...)
; in some files far far away
(make-instance 'baz ...
(baz-bar ...)
(mapcar #'baz-name ...

that kind of things.

Slobodan Blazeski

unread,
Mar 13, 2010, 11:17:16 AM3/13/10
to
On Mar 13, 4:12 pm, Pascal Costanza <p...@p-cos.net> wrote:
> On 13/03/2010 15:55, Slobodan Blazeski wrote:
>
> > I'm writing a lot of CLOS code and with that the usual problem of
> > renaming arrives so I'm interested what kind of functionality does
> > Slime/Emacs has for refactoring common lisp code? Is there anything
> > like Eclipse Refactoring functionality?
>
> As far as I know, not a lot has been done with regard to refactoring for
> Lisp dialects. A reason may be that the regular structure of source code
> in Lisp already makes it very easy to move code around (in advanced text
> editors) that the added value you could get from a refactoring tool is
> not that big anymore.
Either advanced editors have some features that I'm not aware of or I
couldn't disagree more with above statement. I'm really interested how
could an advanced editor help me with renaming a function or changing
a struct into a class. I'm fixing names by hand in every file and that
makes me feel that I'm stuck in the past. And thanks god that I wrote
tests because without them I won't have any clue what I'm doing since
the only visual clue of my project structure comes from the speedbar
and speedbar doesn't show me where my code has errors because I made
some changes with the renaming and moving code from one file to the
other one.

Slobodan

Ole Arndt

unread,
Mar 13, 2010, 12:58:13 PM3/13/10
to

Slobodan Blazeski <slobodan...@gmail.com> writes:

> On Mar 13, 4:12 pm, Pascal Costanza <p...@p-cos.net> wrote:
>> On 13/03/2010 15:55, Slobodan Blazeski wrote:
>>
>> > I'm writing a lot of CLOS code and with that the usual problem of
>> > renaming arrives so I'm interested what kind of functionality does
>> > Slime/Emacs has for refactoring common lisp code? Is there anything
>> > like Eclipse Refactoring functionality?
>>
>> As far as I know, not a lot has been done with regard to refactoring for
>> Lisp dialects. A reason may be that the regular structure of source code
>> in Lisp already makes it very easy to move code around (in advanced text
>> editors) that the added value you could get from a refactoring tool is
>> not that big anymore.
>
> Either advanced editors have some features that I'm not aware of or I
> couldn't disagree more with above statement. I'm really interested how
> could an advanced editor help me with renaming a function or changing
> a struct into a class.

With Emacs:

M-x find-dired RET
Run find in directory: ~/src/lisp/project
Run find (with args): -name '*.lisp'

gives you a dired buffer with all your lisp sources. Alternatively you
can use the REPL command browse-system and then insert your source
directories into the buffer with 'i'. Now you press

t (dired-toggle-marks)
Q (dired-do-query-replace-regexp)
Query replace regexp in marked files: \boldname RET
Query replace regexp in marked files \boldname with: newname RET

and it does an interactive search/replace on all your lisp files.

Ole

--
Ole Arndt http://www.sugarshark.com

Slobodan Blazeski

unread,
Mar 13, 2010, 1:40:23 PM3/13/10
to
On Mar 13, 6:58 pm, Ole Arndt <o...@sugarshark.com> wrote:
Very unsafe and not what I wanted but it'll do.


thanks
Slobodan

Pascal J. Bourguignon

unread,
Mar 13, 2010, 1:49:04 PM3/13/10
to
Slobodan Blazeski <slobodan...@gmail.com> writes:

Well, you see, it would depend on your personal style. Personnaly, I
don't prefix my accessor names by any class name (in general).


But I guess in your case you could write something like:


(unless (fboundp 'concat)
(defun concat (&rest strings)
(apply (function concatenate) 'string strings)))

(unless (fboundp 'symbol-package)
(defun symbol-package (symbol)
(declare (ignore symbol))
obarray))


(defun rename-accessor (accessor-name old-class-name new-class-name)
(let ((aname (symbol-name accessor-name))
(oname (symbol-name old-class-name))
(nname (symbol-name new-class-name)))
(if (string= (concat oname "-")
(subseq aname 0 (min (length aname) (1+ (length oname)))))
(intern (concat nname (subseq aname (length oname)))
(symbol-package accessor-name))
accessor-name)))


(defun split-slot-clause (slot)
(let ()
(loop
with slot-name = (pop slot)
with readers = '()
with writers = '()
with accessors = '()
with others = '()
for (k v) on slot by (function cddr)
do (case k
((:reader) (push v readers))
((:writer) (push v writers))
((:accessor) (push v accessors))
(otherwise (push k others) (push v others)))
finally (return (values slot-name (nreverse readers) (nreverse writers) (nreverse accessors) (nreverse others))))))


(defun rename-defclass-form (defclass-form new-name)
(destructuring-bind (defclass old-name superclasses slots
&rest options) defclass-form
`(defclass ,new-name ,superclasses
,(mapcar
(lambda (slot)
(if (atom slot)
slot
(multiple-value-bind (slot-name readers writers accessors others)
(split-slot-clause slot)
`(,slot-name
,@(mapcan (lambda (accessor-name)
(list :accessor (rename-accessor accessor-name old-name new-name)))
accessors)
,@(mapcan (lambda (accessor-name)
(list :reader (rename-accessor accessor-name old-name new-name)))
readers)
,@(mapcan (lambda (accessor-name)
(list :writer (rename-accessor accessor-name old-name new-name)))
writers)
,@others))))
slots)
,@options)))


(defun pretty-print-defclass (defclass-form)
(destructuring-bind (defclass class-name superclasses slots
&rest options) defclass-form
(princ "(") (princ defclass) (princ " ") (princ class-name) (princ " ")
(if superclasses
(prin1 superclasses)
(princ "()"))
(terpri)
(princ " (")
(let ((firstp t))
(dolist (slot slots)
(if firstp
(setf firstp nil)
(progn (terpri) (princ " ")))
(prin1 slot)))
(princ ")")
(when options
(dolist (option options)
(terpri)
(princ " ") (prin1 option)))
(princ ")")
(terpri)))


(defun rename-class-at-point (new-name)
(interactive "SNew class name: ")
(let* ((start (point))
(defclass-form (form-at-point))
(end (progn (forward-sexp) (point)))
(new-defclass-text (with-output-to-string
(pretty-print-defclass
(rename-defclass-form defclass-form new-name)))))
(delete-region start end)
(insert new-defclass-text)))

--
__Pascal Bourguignon__

Pascal J. Bourguignon

unread,
Mar 13, 2010, 1:50:08 PM3/13/10
to
Slobodan Blazeski <slobodan...@gmail.com> writes:


Advanced editors include a standard programming language to let you
implement automatic code transformations yourself (or load modules
implemented by others to do so).

--
__Pascal Bourguignon__

Pascal J. Bourguignon

unread,
Mar 13, 2010, 1:42:36 PM3/13/10
to
Slobodan Blazeski <slobodan...@gmail.com> writes:

Well, you see, it would depend on your personal style. Personnaly, I

Slobodan Blazeski

unread,
Mar 13, 2010, 2:17:56 PM3/13/10
to
On Mar 13, 7:50 pm, p...@informatimago.com (Pascal J. Bourguignon)
wrote:

Editors, no matter how advanced, are just editors, integration with
rest of the tools gives them power or rather convenience I want. I
rely heavily on Visual clues like project structure, marking files
containing errors with X icons etc. Writing all the functionality that
I take for granted in lesser languages IDEs will take a lot of time
and I'd rather work on my projects. So I'll have to settle with
whatever I could get.

Slobodan
>
> --
> __Pascal Bourguignon__

Slobodan Blazeski

unread,
Mar 13, 2010, 2:31:47 PM3/13/10
to
On Mar 13, 7:42 pm, p...@informatimago.com (Pascal J. Bourguignon)
wrote:

I usually start with class as prefix in the beginning when the
situation is fluid and class is relatively new. Usually when code
proves itself that its staying in my codebase I refactor it and choose
better names and then add documentation and/or clean up the existing
one. Going through several rewrites is one of the things that
characterizes my style that's why renaming is very important to me.
Though Eclipse sucks in pretty much every category it could do wonders
with refactoring.

Slobodan

Pascal J. Bourguignon

unread,
Mar 13, 2010, 3:42:28 PM3/13/10
to
Slobodan Blazeski <slobodan...@gmail.com> writes:

I don't really care about Eclipse. I would have prefered a comment
about the emacs lisp code I proposed...

--
__Pascal Bourguignon__

Pascal Costanza

unread,
Mar 13, 2010, 4:51:09 PM3/13/10
to

No, to the contrary, this is actually much safer than what you get with
typical refactoring tools.

Consider the following example in Java: Say you have defined a class
"Foo", and what to rename it to "Baz". The refactoring tool will replace
all occurrences of, for example, "new Foo()" by "new Baz()". However, it
will miss something like the following:

Class.forName("Foo").newInstance();

With a reliable query/replace mechanism, you won't miss that.

Reflection in programming languages is a reality, and is widely used in
practice, even in lesser languages.

Tamas K Papp

unread,
Mar 14, 2010, 3:44:37 AM3/14/10
to

See http://trittweiler.blogspot.com/2010/03/slime-tidbits-2010-03-05.html

I don't know Eclipse, so I can't say if this is what you are looking for,
but I find these very useful.

Tamas

Slobodan Blazeski

unread,
Mar 14, 2010, 4:00:19 AM3/14/10
to
On Mar 13, 9:42 pm, p...@informatimago.com (Pascal J. Bourguignon)
wrote:
>

> I don't really care about Eclipse.  I would have prefered a comment
> about the emacs lisp code I proposed...

Thank you for your code but its too advanced for me(*) to understand
it completely in one reading and be able to comment on it.
I'm in a middle of lisping for my project but I'll spent few hours
afterward in order to (I hate this word) grok it.

Slobodan
>
> --
> __Pascal Bourguignon__

(*)
http://slobodanblazeski.blogspot.com/2009/06/dissecting-masterpiece.html

Slobodan Blazeski

unread,
Mar 14, 2010, 4:07:07 AM3/14/10
to
On Mar 13, 10:51 pm, Pascal Costanza <p...@p-cos.net> wrote:
>
> No, to the contrary, this is actually much safer than what you get with
> typical refactoring tools.
I beg you pardon, what if I have functions with same names but in
different packages?

>
> Consider the following example in Java: Say you have defined a class
> "Foo", and what to rename it to "Baz". The refactoring tool will replace
> all occurrences of, for example, "new Foo()" by "new Baz()". However, it
> will miss something like the following:
>
> Class.forName("Foo").newInstance();
>
> With a reliable query/replace mechanism, you won't miss that.
>
> Reflection in programming languages is a reality, and is widely used in
> practice, even in lesser languages.
I don't know the mechanism is so cumbersome that I use it only when I
really must to or when I'm really tired to copy paste the same code 40
times like in our database tests.

And BTW I'm starting to believe that lack of good tools for
refactoring of common lisp has something to do with package system.

Slobodan

Slobodan Blazeski

unread,
Mar 14, 2010, 4:09:37 AM3/14/10
to
On Mar 14, 8:44 am, Tamas K Papp <tkp...@gmail.com> wrote:
> On Sat, 13 Mar 2010 06:55:50 -0800, Slobodan Blazeski wrote:
> > I'm writing a lot of CLOS code and with that the usual problem of
> > renaming arrives so I'm interested what kind of functionality does
> > Slime/Emacs has for refactoring common lisp code? Is there anything like
> > Eclipse Refactoring functionality?
>
> Seehttp://trittweiler.blogspot.com/2010/03/slime-tidbits-2010-03-05.html

>
> I don't know Eclipse, so I can't say if this is what you are looking for,
> but I find these very useful.
Thanks like it says in the post it is "Poor man's refactoring tool"
but I'll take what I can get.


Slobodan
>
> Tamas

Alex Mizrahi

unread,
Mar 14, 2010, 4:56:36 AM3/14/10
to
SB> I usually start with class as prefix in the beginning when the
SB> situation is fluid and class is relatively new.

I thought the whole idea of generic functions is that they do dispatch on
class themselves so you don't have to.
If you're prefixing, you're doing GF's job, essentially.

SB> Usually when code proves itself that its staying in my codebase I
SB> refactor it and choose better names and then add documentation and/or
SB> clean up the existing one.

I've personally settled on this style for accessors/readers/writers:

(defclass foo
((author :accessor author-of :initarg :author ...)
(title :accessor title-of ...))

This way I can be sure that xxx-of is a generic function, because it is very
unusual for a normal function to be like xxx-of. And if it is a generic
function, I just don't care about name collisions -- even if unrelated
classes have same slots, it is not a problem because GF dispatch will do
that.
It is also no-brainer -- I can just type code without thinking about fancy
accessor names. And I don't need to remember how exactly did I name accessor
for a certain slot.
And I think it is nice that you can instantly spot accessors in the code.

And I think no refactoring takes less time then refactoring with even the
best tools :).

Helmut Eller

unread,
Mar 14, 2010, 5:00:31 AM3/14/10
to

No. To do that we'd need a database/indexer to model the source base;
it'd be very heavyweight, language specific, and a lot of work. Macros
and reader macros pose special problems. I've heard that macros make it
hard/impossible to write good C++ IDEs. It's easier for Java but I
think Eclipse needs more than 200 MB to build that model for a Hello
World style project (including the JDK classes). Much more for big
projects. This kind of IDE is only possible if you have decades of man
years to spare. Not a path we/I want to pursue.

Try to look at it this way: no Linux kernel hacker uses an IDE.

Helmut

Alex Mizrahi

unread,
Mar 14, 2010, 5:36:44 AM3/14/10
to
SB> Very unsafe and not what I wanted but it'll do.

It is interactive so you can see what you're doing, how is this unsafe?

SB> I beg you pardon, what if I have functions with same names but in
SB> different packages?

SB> And BTW I'm starting to believe that lack of good tools for
SB> refactoring of common lisp has something to do with package system.

Not at all, did you notice cross-reference menu in SLIME?

It works on symbol level (and on CL side) and so there is no problem with
packages.
E.g. for this file:

----
(in-package :foo)

(defun baz () (+ 2 2))

(in-package :bar)

(defun rxx ()
(+ 3
(foo::baz)))

(defun tsd ()
(baz))

(defun baz ()
(+ 3 3))
----

If I place cursor on first definition of baz and hit "Who calls" it shows me
function bar::rxx.
But if I place cursor on the second definition, it shows be that tsd calls
it.

So it very well understands packages. Even if you use multiple packages in
same file.

Also note that it not only shows what file and what function it is, it can
jump directly to the call!
Here's a screenshot: http://i.imgur.com/2Z9MQ.png

I'm pretty sure you can build refactoring tools on top of this. I don't
know, maybe a new version of SLIME already has this, I have a very very old
SLIME here.
But, frankly, using cross-reference tools like "who calls" directly is sort
of enough for me.

Pascal J. Bourguignon

unread,
Mar 14, 2010, 7:38:34 AM3/14/10
to
Slobodan Blazeski <slobodan...@gmail.com> writes:

> On Mar 13, 9:42 pm, p...@informatimago.com (Pascal J. Bourguignon)
> wrote:
>>
>> I don't really care about Eclipse.  I would have prefered a comment
>> about the emacs lisp code I proposed...
>
> Thank you for your code but its too advanced for me(*)

(*^.^*)

> to understand it completely in one reading and be able to comment on
> it. I'm in a middle of lisping for my project but I'll spent few
> hours afterward in order to (I hate this word) grok it.

Don't hesiate to ask if you need help explaining the posted code.

The only funny thing I did on this one, is:

(unless (fboundp 'concat)
(defun concat (&rest strings)
(apply (function concatenate) 'string strings)))

(unless (fboundp 'symbol-package)
(defun symbol-package (symbol)
(declare (ignore symbol))
obarray))

which are here to be able to load and run it on both emacs lisp and
Common Lisp. Apart from the rename-class-at-point command which would
need "libraries" on Common Lisp, but you can call directly:

(pretty-print-defclass
(rename-defclass-form '(defclass name (a b)
(slot1
name-slot0
(slot2 :accessor name-slot2)
(slot3 :reader name-slot3 :reader unchanged
:writer name-set-slot3 :writer set-unchanged
:accessor name-accessor-3 :accessor named-accessor))
(:documentation "An example class"))
'example))

in Common Lisp.


> (*) http://slobodanblazeski.blogspot.com/2009/06/dissecting-masterpiece.html

--
__Pascal Bourguignon__

jos...@lisp.de

unread,
Mar 14, 2010, 8:07:20 AM3/14/10
to
On 14 Mrz., 09:07, Slobodan Blazeski <slobodan.blaze...@gmail.com>
wrote:

> On Mar 13, 10:51 pm, Pascal Costanza <p...@p-cos.net> wrote:
>
> > No, to the contrary, this is actually much safer than what you get with
> > typical refactoring tools.
>
> I beg you pardon, what if I have functions with same names but in
> different packages?
>
> > Consider the following example in Java: Say you have defined a class
> > "Foo", and what to rename it to "Baz". The refactoring tool will replace
> > all occurrences of, for example, "new Foo()" by "new Baz()". However, it
> > will miss something like the following:
>
> > Class.forName("Foo").newInstance();
>
> > With a reliable query/replace mechanism, you won't miss that.
>
> > Reflection in programming languages is a reality, and is widely used in
> > practice, even in lesser languages.
>
> I don't know the mechanism is so cumbersome that I use it only when I
> really must to or when I'm really tired to copy paste the same code 40
> times like in our database tests.

In Zmacs I would use something like M-x Multiple Edit Callers .
It asks for a bunch of functions and then lets me walk
through the calling functions and change those.

>
> And BTW I'm starting to believe that lack of good tools for
> refactoring of common lisp has something to do with package system.

No, it's more its dynamic nature. Macros, code as data, ...

Slobodan Blazeski

unread,
Mar 14, 2010, 8:14:13 AM3/14/10
to
On Mar 14, 10:36 am, "Alex Mizrahi" <udode...@users.sourceforge.net>
wrote:

>  SB> Very unsafe and not what I wanted but it'll do.
>
> It is interactive so you can see what you're doing, how is this unsafe?
Because after third reference I got bored and replace the rest of the
references without paying any attention. That's the same way I use
Eclipse and rely on errors that occur afterward to see what have I
screwed up.

Slobodan

Pascal Costanza

unread,
Mar 14, 2010, 9:29:08 AM3/14/10
to
On 14/03/2010 09:07, Slobodan Blazeski wrote:
> On Mar 13, 10:51 pm, Pascal Costanza<p...@p-cos.net> wrote:
>>
>> No, to the contrary, this is actually much safer than what you get with
>> typical refactoring tools.
> I beg you pardon, what if I have functions with same names but in
> different packages?

I haven't said that you should stop to be careful about what you're doing?

On the other hand, when was the last time this actually happened to you?

>> Consider the following example in Java: Say you have defined a class
>> "Foo", and what to rename it to "Baz". The refactoring tool will replace
>> all occurrences of, for example, "new Foo()" by "new Baz()". However, it
>> will miss something like the following:
>>
>> Class.forName("Foo").newInstance();
>>
>> With a reliable query/replace mechanism, you won't miss that.
>>
>> Reflection in programming languages is a reality, and is widely used in
>> practice, even in lesser languages.

> I don't know the mechanism is so cumbersome that I use it only when I
> really must to or when I'm really tired to copy paste the same code 40
> times like in our database tests.

See, there are unlikely cases that you may miss with a supposedly
"reliable" refactoring tool, and there are unlikely cases that you may
miss with a simple query/replace mechanism. They are both on par.

But there are a number of important questions involved:

+ Are you subconsciously restricting your programming style so that you
can use the supposedly reliable refactoring tool reliably? Maybe
reflective features sometimes make tasks a lot easier to solve than
without them?

+ The same in Lisp world: Maybe we are subconsciously restricting our
programming styles so that we can use query/replace more reliably. But
hey, there is no real loss in expressiveness by making names as unique
as possible, even across packages. That's a very different level of
restriction than deciding not to use reflection.

+ Finally: What about third-party code that you have to integrate with?
In the Java reflection case, it may be necessary that you add class
names in XML configuration files, so that other libraries can load them
by way of Class.forName. The refactoring tool can probably not deal with
that. In the Lisp case, a third-party library may use the same name for
an identifier that you want to use as well. So you have to be careful in
using query/replace here. So again, both approaches are on the same page.

There is really no strong reliability here. Really not.

> And BTW I'm starting to believe that lack of good tools for
> refactoring of common lisp has something to do with package system.

Nope, the main reason is that Lisp is an extensible language, so it's
hard to predict what a symbol actually means.

Slobodan Blazeski

unread,
Mar 14, 2010, 10:40:32 AM3/14/10
to

I'm not a Shrink but I doubt that I subconsciously avoid to use
reflection. I just find its mechanism very cumbersome, but when
reflection is the right tool I use it. On the other hand good names
are hard to find and usually "All the good ones are taken" so I'm
definitely not going to write worse names so I could make my life
easier when I'm gonna need to refactor that piece of code. Though I
have a bad habit of golfing I'm rarely ready to pay with clarity.
All your counter examples came from reflection, well even if I use
third party classes I don't usually refactor those classes but mine
classes. And though refactoring might not work well with reflection
(or AspectJ to help your argument) still it works excellent most of
the time. Our tools are primitive in this domain and there is no shame
in admitting that as there are no billions put in lisp ides. Beside
refactoring IDE is just an convenience functionality, I could live
without it and work with text replacement, afterward many of the code
will be generated by the macros anyway so I will miss those features
even less and I write tests so there is always something that will
punch my head when I do something. All that being said I'm still not
going to deceive myself that in that area lesser languages are more
advanced then we are regardless of how we look on the situation. The
best approach is just to admit it and pick up the low hanging fruits
with resources we have.

The grape is not sour.


Slobodan

Pascal Costanza

unread,
Mar 14, 2010, 11:11:37 AM3/14/10
to

Maybe I just used too many words.

What you're saying is that refactoring works excellent most of the time.

What I'm saying is that query/replace works excellent most of the time.

I mean it.

That's all.

0 new messages