Note that there are some explanatory texts on larger screens.

plurals
  1. POIdiomatic MetaProgramming
    text
    copied!<p>While the web abounds with resources extolling the myriad metaprogramming capabilities of Groovy, I've yet to find anything close to a comprehensive "best-practices" guide for the actual use of such features.</p> <p>Aside from the typical <em>caveat emptor</em> warning of excessive usage, the most specific piece of advice I've read suggests using categories in favor of augmenting metaclasses when possible (which is really just another way of reinforcing the old idiom of 'limited scope').</p> <p>Common sense has been sufficient for my trivial projects, but I'm increasingly concerned about building from potentially poor / inconsistent precedents as I tackle more ambitious tasks.</p> <p>Thus, I would greatly appreciate any advice, resources, or concrete examples of Groovy (or even language-agnostic - My admittedly brief experience with Ruby left me similarly wanting) metaprogramming best-practices.</p> <p>To clarify the topic, I'll provide a (highly) simplified Rational number project that could employ metaprogramming in several different ways:</p> <pre><code>@Immutable class Rational{ int num, den Rational multiply(Integer v){ new Rational(num:num*v, den:den) } } assert new Rational(num:1, den:2) * 3 == new Rational(num:3, den:2) </code></pre> <p>Yet, attempting <code>3*new Rational(num:1, den:2)</code> would obviously produce a MissingMethodException.</p> <p>The simplest, and arguably least fragile means of adding the communicative property would be with a static initializer block in the Rational class:</p> <pre><code>static { Integer.metaClass.multiply = {Rational fraction -&gt; fraction*delegate} } ... assert 3*new Rational(num:1, den:2) == new Rational(num:3, den:2) </code></pre> <p>But this is consequently global in effect, and rather rigid.</p> <p>A more versatile, and perhaps more organized approach would be with some kind of optional bootstrapping:</p> <pre><code>class BootStrap{ static void initialize(){ Integer.metaClass.multiply = {Rational fraction -&gt; fraction*delegate} } } </code></pre> <p>Now we have the option of enabling the feature(s) we wish to have. This, however, could result in all manner of dependency issues.</p> <p>And then there's categories.. Safely explicit, but not exactly convenient:</p> <pre><code>@Category(Integer) class RationalCategory{ Rational multiply(Rational frac){ frac*this } } use(RationalCategory){ assert 3*new Rational(num:1, den:2) == new Rational(num:3, den:2) } </code></pre> <p>In general, I find I modify metaclasses when I'm <em>adding</em> new behavior, but use categories when I may be <em>changing</em> existing behavior. Overriding the division operator to produce a Fraction, for instance, would be best contained inside a category. Thus, solutions 1 or 2 would be 'acceptable' as I'm merely appending behavior to the Integer class, not altering typical usage.</p> <p>Does anyone agree/disagree with that sentiment? Or perhaps know of some superior methodology? (I omitted mixins here, I realize.)</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload