Nick Sieger: Metaprogramming Pattern No. 1: Self-specialize http://blog.nicksieger.com/articles/2006/07/11/metaprogramming-pattern-1-self-specialize en-us 40 Metaprogramming Pattern No. 1: Self-specialize <p>One of the biggest aspects of Ruby that I&#8217;ve been digging are the metaprogramming facilities, many of which draw from the &#8220;code as data&#8221; philosophy that comes from Lisp&#46; Metaprogramming has become somewhat of a buzzword in the Ruby community, about as popular as &#8220;domain specific language&#8221; in terms of its presence in the titles of conference presentations and the like&#46;</p> <p>So it seems to me that, while a good many smart people are talking and writing about metaprogramming, that we haven&#8217;t yet started cataloguing all the different techniques in a shareable way&#46; Is it time to start writing a catalog of metaprogramming patterns? Or do I risk being <a href="http://www.c2.com/cgi/wiki?ShowTrialOfTheGangOfFour">taken to trial</a> for attempting to unveil the rubyist&#8217;s magic tricks?</p> <p>No, what I am really looking for is the metaprogramming <a href="http://domaindrivendesign.org/discussion/messageboardarchive/UbiquitousLanguage.html">ubiquitous language</a>&#46; Ruby has a lot of language&#45;level features that facilitate metaprogramming, but without a strong jargon to describe what&#8217;s going on&#46; Sorry, but <code>module_eval</code>, <code>instance_eval</code>, metaclass vs&#46; singleton class vs&#46; eigenclass, <code>method_missing</code> etc&#46; just don&#8217;t cut it&#46; So here&#8217;s a first shot at re&#45;invigorating the conversation and taking it to the &#8220;HNL&#8221; (&#8216;hole nuther level)&#46; So herewith begins Metaprogramming Pattern No&#46; 1: Self&#45;specialize&#46;</p> <p><em>Why is this one number 1? No reason, pretty arbitrary&#46; I haven&#8217;t taken the time to document any more yet&#46; This will be an ongoing project for me&#46;</em></p> <h2>Self&#45;specialize</h2> <p>You have an algorithm or a &#8220;method object&#8221; that you wish to make flexible by parameterizing with additional code and/or data that isn&#8217;t known at the time you wrote the original class definition&#46;</p> <p><em>Therefore</em>, write a method that redefines itself in the singleton class of the currently instantiated object, then re&#45;sends the message to the customized method&#46; In a way, this is simply <em>memoizing</em> the method body itself as a speed&#45;up&#46;</p> <p>Trivial example:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">class </span><span class="class">Foo</span> <span class="keyword">def </span><span class="method">initialize</span><span class="punct">(</span><span class="ident">p</span><span class="punct">)</span> <span class="attribute">@prefix</span> <span class="punct">=</span> <span class="ident">p</span> <span class="keyword">end</span> <span class="keyword">def </span><span class="method">result</span><span class="punct">(</span><span class="ident">val</span><span class="punct">)</span> <span class="ident">specialize_result</span> <span class="ident">result</span> <span class="ident">val</span> <span class="comment"># send the message again</span> <span class="keyword">end</span> <span class="ident">private</span> <span class="keyword">def </span><span class="method">specialize_result</span> <span class="ident">method_decl</span> <span class="punct">=</span> <span class="punct">&quot;</span><span class="string">def result(val); </span><span class="punct">&quot;</span><span class="comment">#{@prefix}: #{val}&quot;; end&quot;</span> <span class="ident">instance_eval</span> <span class="ident">method_decl</span><span class="punct">,</span> <span class="punct">&quot;</span><span class="string">generated code (<span class="expr">#{__FILE__}</span>:<span class="expr">#{__LINE__}</span>)</span><span class="punct">&quot;</span> <span class="keyword">end</span> <span class="keyword">end</span> <span class="ident">f</span> <span class="punct">=</span> <span class="constant">Foo</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(&quot;</span><span class="string">foo</span><span class="punct">&quot;)</span> <span class="ident">f</span><span class="punct">.</span><span class="ident">result</span><span class="punct">(&quot;</span><span class="string">hi</span><span class="punct">&quot;)</span> <span class="comment"># =&gt; &quot;foo: hi&quot;</span> <span class="ident">g</span> <span class="punct">=</span> <span class="constant">Foo</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(&quot;</span><span class="string">bar</span><span class="punct">&quot;)</span> <span class="ident">g</span><span class="punct">.</span><span class="ident">result</span><span class="punct">(&quot;</span><span class="string">hi</span><span class="punct">&quot;)</span> <span class="comment"># =&gt; &quot;bar: hi&quot;</span></code></pre></div> <p>Why would you do this rather than writing the logic into the original method? It&#8217;s hard to justify use of the technique in the example above&#46; But it could be used to DRY up tedious, redundant code that for performance reasons you would prefer to have inlined rather than invoking an additional instance method&#46;</p> <p>OK, so honestly I can&#8217;t come up with a decent reason to do this yet; I haven&#8217;t done enough metaprogramming to have had the need for it&#46; Still, it seems like a nifty enough trick and maybe it will come in handy for you&#46; This does show you just how dynamic Ruby&#8217;s method resolution is, that you can suddenly define and call a different method implementation inside of the method itself!</p> <p>This technique was spotted in the rewritten <a href="http://dev.rubyonrails.org/svn/rails/trunk/actionpack/lib/action_controller/routing.rb">routes</a> implementation for Rails 1&#46;2 (currently on the <a href="http://dev.rubyonrails.org/svn/rails/trunk/">trunk</a>) &#45;&#45; see the <code>#write_generate</code> and <code>#write_recognize</code> methods&#46;</p> <p><em>Update</em>: Good timing&#46; _why just posted <a href="http://redhanded.hobix.com/inspect/methodsThatSelfDestruct.html">much more succinct description</a> of the same phenomenon, with better examples&#46;</p> Tue, 11 Jul 2006 03:48:00 +0000 urn:uuid:dc66b0b8-def5-4be6-9b1e-1a2e883c6106 Nick Sieger http://blog.nicksieger.com/articles/2006/07/11/metaprogramming-pattern-1-self-specialize ruby rails metaprogramming pattern http://blog.nicksieger.com/articles/trackback/33