Reflexive metaprogramming have been part of programmer consciousness for a long time. It’s been possible in many languages in one way or another. Some have embraced it more than other, and among these the most prominent are Lisp, SmallTalk, Python and Ruby. But it’s not until Ruby entered the common programmers mind that Metaprogramming actually starts to become common place. The discussion on DSL’s is also relevant for metaprogramming issues, since implementing a DSL (in the same language, of course) is very hard without reflexive metaprogramming.
I recently reread my copy of Refactoring, and as usual I was amazed by how on-topic it was, and how easy and useful the tips in it where. But, I also started thinking that there is something missing. Refactoring is specifically about Object Oriented Programming, but I’m heading more and more towards Language Oriented Programming, with DSL’s, reflexive metaprogramming, introspection and Meta-class extensions, These approaches make the base OOP system much more powerful. This is also very prominent when prototyping smaller
systems. I find that I start by writing methods in such a way that I have to do very much by hand, and in the next stage I fold my code, as much as possible, both for readability and laziness.
What is this blog about, then? Well, I propose that the time is right and nigh for a catalog of Metaprogramming Refactorings. I’m not saying I should do them, not at all, but it would be something very nice to have. Maybe as a wiki somewhere, where some of our metaprogramming luminaries could write about their experiences. DHH? _why? Weirich? Dave Thomas?
Anyway, just to make it very apparent what kind of refactorings I’m talking about, I will provide a somewhat contrived example. This is more or less what a mock implementation of something could look like. Some log calls, and quite much repetition.
def startup
@log.info { "-startup()" }
self.startup_foo
self.startup_bar
end
def init
@log.info { "-init()" }
self.init_vars
self.init_constants
self.init_other
end
def main
@log.info { "-main()" }
self.run_main
puts "hello from main"
end
def close
@log.info { "-close()" }
self.close_bar
self.close_foo
end
def shutdown
@log.info { "-shutdown()" }
self.all_shutdown
self.run_shutdown
end
I present the Extract Code Template metarefactoring. The first step is to take all the method names that should be handled and put these in a list, like this:
[:startup, :init, :main, :close, :shutdown]
Then we walk through these definitions, and provide empty bodies for each, like this:
[:startup, :init, :main, :close, :shutdown].each do |name|
define_method(name) do
end
end
We then have to change the list to a hash, making each method-name point to the methods to call in the method, like this:
{ :startup => [:startup_foo, :startup_bar],
:init => [:init_vars, :init_constants, :init_other],
:main => [:run_main],
:close => [:close_bar, :close_foo],
:shutdown => [:all_shutdown, :run_shutdown] }
When this has been done, we have to add the part of the main method which can’t be extracted in the same way, which we do with a proc:
{ :startup => [:startup_foo, :startup_bar],
:init => [:init_vars, :init_constants, :init_other],
:main => [:run_main, lambda { puts "hello from main" }],
:close => [:close_bar, :close_foo],
:shutdown => [:all_shutdown, :run_shutdown] }
The next step is to walk through the method names and values, and define the method contents. We then remove the original methods. Finally, our code may look like this:
{ :startup => [:startup_foo, :startup_bar],
:init => [:init_vars, :init_constants, :init_other],
:main => [:run_main, lambda { puts "hello from main" }],
:close => [:close_bar, :close_foo],
:shutdown => [:all_shutdown, :run_shutdown] }.each do |name, methods|
define_method(name) do
@log.info { "-#{name}()" }
methods.each do |m|
if m.is_a? Proc
m.call
else
self.send m
end
end
end
end
Now, in this case I’m not sure I would do this refactoring at all. This serves more as an example of the kinds of refactorings I would like to see in a catalog like this. Refactorings like Extract DSL, Create Class Dynamically, Extend From Anonymous Class and others. This is something I really feel would be useful in today’s programming environment.
Comments and tips are very welcome.
17 Comments | By Ola Bini | In: Uncategorized | tags: Uncategorized. | #