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.