Best practices for Ruby Meta-programming

Dear all,

Ruby meta-programming is a real fun.
However no power comes without responsibility, so does the Ruby’s meta-
programming.
Can anybody share the experiences with meta-programming (for example,
in writing DSLs.) so that if possible we can come up with best
practices?
It would be great if somebody is already aware of such best practices.

Thanks.

Warm regards,
Harshal

harshal wrote:

Dear all,

Ruby meta-programming is a real fun.
However no power comes without responsibility, so does the Ruby’s meta-
programming.
Can anybody share the experiences with meta-programming (for example,
in writing DSLs.) so that if possible we can come up with best
practices?

Don’t be evil.

Don’t be stupid.

I think that about covers it.


James B.

www.risingtidesoftware.com - Wicked Cool Coding
www.jamesbritt.com - Playing with Better Toys

On Thu, Mar 6, 2008 at 1:15 PM, James B. [email protected]
wrote:

Don’t be evil.

Don’t be stupid.

So THAT’s what I’ve been doing wrong all this time. Wish someone had
clued me in sooner.

My concrete contribution to this discussion:

Modules are your friend. When tempted to dynamically extend a class
with new methods, consider adding them to a module instead
(potentially a dynamically created module using Module.new). Then use
#include or #extend to add the module to your target. This has a
number of benefits, including:

  • Clients can clearly see which which methods come from your
    extension vs. the methods that are part of the class itself, by using
    Ruby’s reflection.
  • Clients can override dynamically created methods and still reuse
    them with #super() - no need to resort to aliasing.
  • A module can be re-used for either class-wide extension or
    extending a single object.

That’s absolutely true.

It reminds me Einstein’s quote “Make it simple, no simpler” (or
similar).

I think ‘Module’-related suggestion is a good beginning.
Some of the best practices would still be valid from traditional OOAD.

However, the place where Ruby differentiates itself from traditional
programming languages is - Meta-programming.
So I am expecting few ‘new patterns’ just because of meta-programming.
I could not get any chance to look at the book ‘Design patterns in
Ruby’.

Thanks.

Harshal

harshal wrote:

However, the place where Ruby differentiates itself from traditional
programming languages is - Meta-programming.

If Ruby differentiates itself it’s in the form of a particularly
expressive syntax, or maybe in the way it brings an amalgam of different
language features together. Metaprogramming itself is nothing new.
Heck, I used to do metaprogramming in C++.

Avdi G. wrote:

Avdi

Hell … I’ve done metaprogramming in macro assembler and FORTRAN. :wink:

Hi,

harshal wrote:

Can anybody share the experiences with meta-programming (for example,
in writing DSLs.) so that if possible we can come up with best
practices?

Martin F. is crafting a book along these lines:

http://martinfowler.com/dslwip/

Regards,

On Thu, 6 Mar 2008, harshal wrote:

Can anybody share the experiences with meta-programming (for example,
in writing DSLs.) so that if possible we can come up with best
practices?
It would be great if somebody is already aware of such best practices.

Before STL was widely available… I wrote a C++ library sort of
equivalent to STL.

At first I used templates quite heavily.

After awhile I realised I had no idea really WTF C++ was doing when it
instantiated those templates.

So I ripped all the C++ template code out of the library and replaced
it with marked up code that a perl script ate.

The script spat out plain, well formatted vanilla template free C++
code.

MUCH BETTER!! SO MUCH CLEARER! SO MUCH EASIER TO DEBUG! SO MUCH EASIER
FOR CLIENT CODE TO USE, READ AND REUSE!

But before I could become famous…

STL came along…

Bugger.

Lessons learnt :-

  • Meta program sucks. You (and your user) have no idea what you have
    really generated.

    So generate intermediate files of vanilla ruby strings, which you
    can output to file, print, inspect at leisure, understand, test,
    debug and eval.

  • Mark up languages universally suck (yaml, xml, html, sgml,
    …).

    Unless you need a vendor neutral language to communicate with
    another application, with strong schema validation to prove who has
    got it wrong… Don’t use markup languages!

    Ruby is expressive enough, and it’s schema is better known /
    understood.

  • Instantiating code should instantiate the documentation as
    well. Reading templated code is hard, reading templated code with
    generic documentation bends the mind.

  • Perl sucks. Ruby is way better.

  • The guy sitting next door to Matz is going to beat you. (The authors
    of STL worked just down the corridor to Strostrup and convinced him
    to tweak C++ templates into something usable. :slight_smile:

Whilst we’re on the subject…

I once wrote a Domain Specific Language. Full flex / bison lexer
parser + byte code interpreter.

Lessons learnt…

  • No language I can write in a man year or ten will have any where
    near the power and expressivity of Ruby.

  • No language I can write in a man year or ten will be as debugged as
    Ruby.

  • No language I can write in a man year or ten will be as well
    documented/exampled as Ruby.

  • Any program sufficiently large will contain a poorly implemented
    version of Common Lisp.

  • I wish I had Ruby at that time. If I were to redo that project I
    would…

  • Provide a framework of ruby classes to do the hard domain specific
    stuff.

  • And (maybe, if non-techies really really had to read / write this
    stuff) provide the very thinnest layer of syntax sweetening…

  • that translated directly into vanilla ruby intermediate files…
  • that invoked the framework…
  • but permitted use of the full power of Ruby for most of the work.

John C. Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : [email protected]
New Zealand

On Mon, Mar 10, 2008 at 4:06 AM, John C. [email protected]
wrote:
If you use metaprogramming to make your code shorter you subtle point
might be quite valid, and yet you are too categorical for my taste.
Well factored metaprogramming might make your code more readable, do
not forget that
attr_reader e.g. is nothing less than MP.

FWIAC metaprogramming is here to do things at runtime and nothing else
in the world can replace it.
It is however always a valid question to ask if a redesign of the
application might allow us to get rid of MP, but in some cases it is
just not possible, look at some libraries as Ara’s prototype or my
traits library.

You know sometimes it is just the right tool to use, sometimes.

Cheers
Robert


http://ruby-smalltalk.blogspot.com/


Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein

Metaprogramming. Isn’t that just another name for self-modifying code.
Only had to do that once in a game to get past a hardware limitation on
frame-flyback time for a display driver. (Had to split the screen into
1/3rds to get the maximum number of hardware sprites to treble :slight_smile: ) That
was really the only time I had to do it and could justify it
convincingly since we were down to counting t-states.

<FX:Ducks behind sofa and sobs quietly>

On Mon, Mar 10, 2008 at 8:11 AM, Robert D. [email protected]
wrote:

You know sometimes it is just the right tool to use, sometimes.

Cheers
Robert

100% agree.

My 2c on the topic…

I think of eval and other meta-programming tools in Ruby (funny, that
we use the same acronym for monkey-patching) like giving a Nascar to
your 16 year old kid to take his/her driver’s license test in. In the
hands of a skilled driver, however, the car performs some kind of
magic. Or maybe a better analogy would be putting someone behind the
stick of a fighter jet with baby-safe control systems in place to make
things stable, and expect that person to be able to actually fly the
thing. They can at first, until they hit turbulence.

I’m still a kid in that respect, and try to avoid meta-programming as
much as possible (I actually use these features in Ruby quite often;
not much at all in other languages).

Todd

On Tue, Mar 11, 2008 at 2:25 AM, Paul M. [email protected]
wrote:

Metaprogramming. Isn’t that just another name for self-modifying code.
Only had to do that once in a game to get past a hardware limitation on
frame-flyback time for a display driver. (Had to split the screen into
1/3rds to get the maximum number of hardware sprites to treble :slight_smile: ) That
was really the only time I had to do it and could justify it
convincingly since we were down to counting t-states.

<FX:Ducks behind sofa and sobs quietly>
Duck is good, if you really go to the extremes of Ducktyping you need
metaprogramming in Ruby. Modules and Classes just wont do, hopefully I
will have time soon to elaborate on this.
Cheers
Robert

Posted via http://www.ruby-forum.com/.


http://ruby-smalltalk.blogspot.com/


Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein

On Mon, Mar 10, 2008 at 5:41 PM, Todd B. [email protected]
wrote:

I think of eval and other meta-programming tools in Ruby (funny, that
we use the same acronym for monkey-patching) like giving a Nascar to
Sorry for the OT, but I am following Nascar for 20 years now and for
the first time my driver won the 500. Good old Rusty just never did
me that favor :wink: As none in Europe cares I just had to tell you, sorry
for the noise.
Robert

On Mon, 10 Mar 2008, Robert D. wrote:

attr_reader e.g. is nothing less than MP.

I’d still be happier if I could flick a switch and see the
intermediate vanilla code that was generated.

FWIAC metaprogramming is here to do things at runtime and nothing else
in the world can replace it.

I suspect we’ve overloaded the term “meta-programming”.

There are “programs that write programs”.

There is a common subset of that … “programs that instantiate
generic template code by substituting template variables with concrete
text.”

If by meta-programming you mean one of the meanings above this line,
seeing a concrete vanilla ruby intermediate representation is
invaluable.


If by meta-programming you mean one of the following, creating
concrete intermediate representations is harder…, but certainly not
impossible.

There are “programs that query & display the current run time state of
the program”. (Profiling, debugging, coverage, memory profiling & leak
detection, object creation hotspot detection…)

There are very hairy programs “that query the current run time state
of the program and modify the code ‘on-the-fly’ accordingly.”

You know sometimes it is just the right tool to use, sometimes.

Their are times and places where on-the-fly self-modifying code is
appropriate.

They are clearly marked on the map as “Here be Dragons”.

John C. Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : [email protected]
New Zealand

On Tue, Mar 11, 2008 at 5:31 AM, John C. [email protected]
wrote:
You are certainly right that MP is overused here, I have read slightly
different definitions of which I like best:
"Programming for the Programmer.

def attr_reader *atts
atts.each do |att|
define_method att do instance_variable_get “@#{att}” end
end
end

is definitely for the programmer, it also should rather run at runtime
than at compile time.

Robert


http://ruby-smalltalk.blogspot.com/


Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein

programming (for example,
in writing DSLs.) so that if possible we can come up with best
practices?
It would be great if somebody is already aware of such best practices.

Don’t overuse method_missing, it can be conflictive with other
frameworks and dsl in your application.

Pedro Del G.

Email : [email protected]

On Tue, 11 Mar 2008, Robert D. wrote:

def attr_reader *atts
atts.each do |att|
define_method att do instance_variable_get “@#{att}” end
end
end

is definitely for the programmer, it also should rather run at runtime
than at compile time.

This topic is about Best Practices for Meta-Programming.

And you have just handed me a brilliant example of why human readable
intermediate representation is Best Practice…

Hmm. Is
attr_read :foo
really equivalent to a plain getter?

define foo
@foo
end

The way I read your code it’s equivalent to…

define foo
instance_variable_get “@foo
end

Which is probably slower.

Either way the very fact we need to think about it illustrates the
value of human readable intermediate representations!

How about…

def make_getter( att)
"define #{att}
@#{att}
end
"
end

def Module.attr_read( *atts)
atts.each do |att|
self.class_eval( make_getter( att))
end
end

Note I have a bug in make_getter…

The class_eval failed for some mysterious reason, so I said…
puts make_getter(“foo”)
And got…
define foo
@foo
end

Oops! I see the problem…it should be…
def make_getter( att)
"def #{att}
@#{att}
end
"
end

And I get…
puts make_getter(“foo”)
And got…
def foo
@foo
end

Yip that worked.

John C. Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : [email protected]
New Zealand

On Tue, Mar 11, 2008 at 10:40 PM, John C. [email protected]
wrote:

This topic is about Best Practices for Meta-Programming.

And you have just handed me a brilliant example of why human readable
intermediate representation is Best Practice…

Hmm. Is
attr_read :foo
really equivalent to a plain getter?
Semantically yes, for the implementation
I have no idea, it is probably written in C. The point is what it is
good for.
R.

John C. Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : [email protected]
New Zealand


http://ruby-smalltalk.blogspot.com/


Whereof one cannot speak, thereof one must be silent.
Ludwig Wittgenstein