On Thu, Aug 14, 2003 at 05:27:58AM +0900, Simon Strandgaard wrote:
> On Thu, 14 Aug 2003 04:03:51 +0900, Lothar Scholz wrote:

> > Hello Simon,

> > Tuesday, August 12, 2003, 10:44:49 PM, you wrote:


> > Have you ever spend more then a few minutes to think about the
> > problems with refactoring in script languages and especially with
> > ruby ?

> > I guess you didn't.

> I have thought about it for more than 5 minutes, but not done any deep
> analizis of what it would take. I agree with you that there is
> problems with dynamic-typed languages.

> Is there anyone who has done some analizis of what such
> refactoring-tool exactly will require ?   a ruby-in-ruby parser ?

The parser would be the easy part (probably).

Once you have the parser, consider renaming the method Foo#bar to
Foo#foo():

------8<-----
class Foo; def bob; end; end
class Bar; def bob; end; def foo; Foo.new; end; end

a = Foo.new
b = Bar.new
a, b = b, a

a.bob
b.bob
------8<-----

So far, it would be relatively simple to track variable assignment and
work out which variables are instances of Foo and then rename references
from Foo#bob() to Foo#foo

------8<-----
def bar(*args)
	args.each do |obj|
		obj.bob if obj.respond_to?(:bob)
	end
end

bar(a, b, a.bob)
------8<-----

Here it becomes somewhat more difficult - it might be fair to
assume that in most cases the author wishes to rename Foo#bob so that it
isn't called by bar(), but not always.  And how should the refactoring
agent change the code if it isn't?

def bar(*args)
	args.each do |obj|
		if obj.kind_of?(Foo)
			obj.foo if obj.respond_to?(:foo)
		else
			obj.bob if obj.respond_to?(:bob)
		end
	end
end

??

------8<-----
a.foo.bob
------8<-----

And although in the code above, you could determine fairly easily what
type of object will be returned by Bar#foo(), it would be very easy to
think of situations where it wouldn't be easy and, in the case of
compiled C extensions, not possible.

------8<----
str = 'BOB'.downcase

...
b.method(str).to_proc
b.send(str.intern)
------8<----

I won't comment.  Or mention eval.  Or instance_eval.  Or module_eval.
Or subclasses which may or may not define the same method which may or
may not call super().


AIUI, refactoring is supposed not to change the behaviour of the code:
And without creating either a ruby simulator which could run a ruby
script and determine every single variable which could possibly be an
instance of Foo, and then take into account code which is dynamically
generated from external sources, e.g. DRb, marshalled data, class and
function names derived from a database/other programs.  And if you end
up treating almost every variable and function return as a potential
instance of Foo then do you have to refactor the every possible
reference to Foo#bob with an if obj.kind_of?(Foo) ... else ... end?

So in answer to your question, in order to refactor Ruby code it's
probably not possible without a divining rod, some form of mind reading
and lashings of good luck.  And even if you manage a no-side-effects
refactor, will it really acheive the end of making the script more
legible to the programmer?

The problem could be partially solved by designing strict rules for
writing a style of Ruby which could be refactored in well defined ways,
but I don't think that it would be feasible to do for an otherwise
unknown Ruby script.

If, for example, all code was to be duck typed, so that the behaviour of
instance method #foo should be the same in Foo as in Bar, then renaming
would be simpler, because you would normally either be renaming all
references a particular instance method name (regardless of the object
class) or simply renaming the method definition within the class
definition because it no longer conforms to the behaviour specified for
the name.  Even so it would require checking for literals which match
the function name, whether symbols or strings or the contents of arrays
so that you have a chance of catching most obj.__send__(sym).  And the
problem with changing those is then calculating the side effects.

Of course, I have only considered renaming a method which is just one
(relatively simple?) refactoring operation.

> > The fexibility of languages like ruby makes it impossible to ever
> > write something like intellij. And it is not a parser problem. I can
> > write a full ruby parser in a weekend, but this would give me no
> > clue about the behaviour.

> OK, also I look forward to a ruby-in-ruby parser on next monday :-)

Even if he did, it would only be of real use to AEditor for syntax
highlighting and, possibly, automatic recognition of possible folds.

And it shouldn't be hard to do - with StringScanner it should be fairly
straight forward.  Of course it depends what you mean by a 'parser'.  If
you want a function that returns an array of tokens, it shouldn't take
long.  If you want a ruby-in-ruby interpreter, it would take a bit
longer... :)

TTFN,


Geoff.