Ruby meta-programming uses runtime flexibility to do cool things, but has_many is just a method, like any other method, which is passed arguments (at runtime), and can do super-cool things with them because of the late-binding architecture of Ruby. This a concept originated from Smalltalk. It's super cool.
Lisp macros do something completely different: they let you extend the parser of the language. They literally let you write code that edits your code, at compile time, before anything is run. It's super cool, too, but in a very different way.
Here's a hig-level view:
1) You write code.
2) That code is translated to a data structure which is interpreted by a computer.
Lisp macros are ways of extending what the translation rules are. Ruby, on the other hand, has lots of neat design decisions which let you write code that interprets instructions at runtime in interesting ways. Very different.
Sure, the inner workings of the system is as you describe, but the approach this article takes does make sense out of a developer standpoint, no? Sure "has_many" is just a method that the late-binding architecture of Ruby allows us to write, but this means that we can dream up our own DSLs and implement our DSL by doing what we do best - write methods ;) Which is a less general approach than the full-blown Lisp macros guns.
Spot on - they can certainly be used for similar goals (building powerful abstractions). But just as a horse and a bicycle can both be used to get places, but still be different, Ruby meta-programming and Lisp-style macros are very different, even if they are sometimes used to accomplish similar goals.
When you do meta-programming using Ruby, you're writing code that is executed at run-time (and can use runtime data). When you're writing Lisp macros, you're literally writing a code pre-processor: a bunch of little programs that walk over your code and expand it to other code before execution begins.
A great question is what the relative limitations are of both. I don't have a definitive answer, but I think this is one of the fundamental questions software engineering should attempt to answer - what abstractions can we build at compile-time, and what abstractions must be left for run-time?
Yes, the mechanisms are completely different, but from a programmer who hasn't yet had the Lisp revelation, they appear similar. The goal here is to write higher-level code to more clearly describe your solution to a problem. Ruby lets you get part of the way there, which is often good enough, but it hits a wall that macros cross without even glancing back.
Ruby [...] hits a wall that macros cross without even glancing back.
I'm not very comfortable picking sides like that - they're different things (as I said in the other post - horses and bicycles). Which is better for creating abstractions is a very difficult question, which I don't think we quite have the vocabulary to ask correctly.
Decent article...but bad title. Should've been something like "DSLs in Ruby and Lisp...why Macros matter". Regardless, I'd like to see this thread continued.
So I asked this on your blog but comments are moderated, so thought I'd repeat here. I like the article, but would really appreciate some specific examples on how Ruby metaprogramming gets hairy compared to Lisp. I'm a ruby guy of about 2 years, but have only flirted with Lisp through a few small tutorials.
So when do you run into problems with Ruby? Can you give some samples of code that demonstrate the problem, and how lisp's macros avoid it? And, does something like this (http://news.ycombinator.com/item?id=948454) fix the issue on the ruby side?
I haven't gotten far enough to demonstrate the icky part, but I should get there within a few months. I definitely plan on going further with both Ruby and Clojure to see how they compare in the end. I knew this whole “Lisp revelation” thing would hit me at some point, but I doubt I’m going to switch completely. I'd love to post again with both side by side to compare and contrast.
Ruby meta-programming uses runtime flexibility to do cool things, but has_many is just a method, like any other method, which is passed arguments (at runtime), and can do super-cool things with them because of the late-binding architecture of Ruby. This a concept originated from Smalltalk. It's super cool.
Lisp macros do something completely different: they let you extend the parser of the language. They literally let you write code that edits your code, at compile time, before anything is run. It's super cool, too, but in a very different way.
Here's a hig-level view:
1) You write code.
2) That code is translated to a data structure which is interpreted by a computer.
Lisp macros are ways of extending what the translation rules are. Ruby, on the other hand, has lots of neat design decisions which let you write code that interprets instructions at runtime in interesting ways. Very different.