Best practices for Ruby Meta-programming

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 Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : john.carter@tait.co.nz
New Zealand

···

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.

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

···

On Mon, Mar 10, 2008 at 4:06 AM, John Carter <john.carter@tait.co.nz> wrote:

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

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

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 Mon, Mar 10, 2008 at 8:11 AM, Robert Dober <robert.dober@gmail.com> wrote:

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

Cheers
Robert

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.

···

On Mon, 10 Mar 2008, Robert Dober wrote:

----------------------------------------------------------------------

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 Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : john.carter@tait.co.nz
New Zealand

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, Mar 10, 2008 at 5:41 PM, Todd Benson <caduceass@gmail.com> wrote:

On Mon, Mar 10, 2008 at 8:11 AM, Robert Dober <robert.dober@gmail.com> 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

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>

···

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

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

···

On Tue, Mar 11, 2008 at 5:31 AM, John Carter <john.carter@tait.co.nz> wrote:

--
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>

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

···

On Tue, Mar 11, 2008 at 2:25 AM, Paul Mckibbin <pmckibbin@gmail.com> wrote:

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

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

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

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 Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : john.carter@tait.co.nz
New Zealand

···

On Tue, 11 Mar 2008, Robert Dober 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.

> 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?

Semantically yes, for the implementation
I have no idea, it is probably written in C. The point is what it is good for.
R.

···

On Tue, Mar 11, 2008 at 10:40 PM, John Carter <john.carter@tait.co.nz> wrote:

On Tue, 11 Mar 2008, Robert Dober wrote:

John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : john.carter@tait.co.nz
New Zealand

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

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