On 10/26/08, ara.t.howard [email protected] wrote:

equivalent methods only manipulating the syntax tree seems like a real advantage from

here, generating a dsl is already as nearly painless as it could be in

ruby, and methods do in fact have access to local variables and other

caller context

Sorry for the late reply… I thought this thread had died.

You seem to have left out inline methods, for whatever they’re worth.

Yes, DSLs in ruby are currently very easy to create and use; I

anticipate that macros will make DSLs slightly harder to write, but

easier to use. Ruby DSLs tend to be very natural looking to users, but

there are often small compromises to usability that won’t make much

sense to domain users, such as the need to begin some words with a

colon and the need to use ‘do’ at certain places in the language for

no apparent reason. If you use macros to define your DSL, it should be

possible to rid oneself of those features. The situation is already

pretty good, but macros will make it slightly better. I say “will”,

because, to be honest, at the moment most of the sugary convenience

features needed for nicer DSLs are not present.

As an example of a DSL (perhaps the wrong word in this case…) that

could be written in macros, there is iterate for common lisp:

http://common-lisp.net/project/iterate/doc/index.html

Iterate is a looping mini-language with special syntax for many common

looping tasks. It looks kind of like what list comprehensions do for

you in python, but more powerful. Now I imagine that something like

this could be written entirely with methods… but it would be too

slow. (I admit, tho, that I don’t understand iterate – or lisp in

general – very well. If someone out there wants to correct my

misapprehensions, please feel free.)

cfp:~ > cat a.rb

def context &block

eval ‘a += 40’, block

block.call

end

This is a slick way of getting to your caller’s lvars. I would have

passed in a Binding myself, but this way is probably a little cleaner.

But, the caller must pass a block (or binding) in order to make this

work. Sometimes, that’s not a problem. Sometimes it is. For instance

(I’ve run into this) if you want to create an api that works exactly

like Regexp#match, you’ll find that it can’t be done. #match sets its

caller’s $~ (a local variable); methods can’t do that. In the past,

I’ve passed in an optional Binding to handle this case, but

practically speaking, it was a little too clumsy. Using a block

instead is a better idea, but you’re still changing the interface used

by your custom #match. The whole point is to re-use your user’s

existing knowledge about #match… if he has to remember, “oh yeah,

and if you use $~ or other matching variables, you have to pass an

extra block to #match”, then that’s not an effective re-use of

existing knowledge; it might as well be a new interface.

can you show us something that cannot be done using ruby currently,

which macros make possible?

A recently requested new feature on ruby-core was DIR, which acts

like FILE, but returns the directory containing the current source

file. As a macro, that is:

macro DIR

File.dirname FILE )

end

Now maybe (now that I’ve seen your block-as-binding trick) you can

actually write this as a method, something like,

def DIR(&ctx)

File.dirname(eval(“FILE”,ctx))

end

I have no ruby ATM, and can’t check if that works or not, sorry. But

if it does, it will have to be called like DIR{}, instead of

DIR. I’d find that a little jarring.

Another recent request was a ‘with’ keyword, which operates like

instance_eval, but only changes the default receiver for code in the

block passed in, and not self as seen by instance variables. I have an

implementation of this as well (in the example directory of

RubyMacros), but for various reasons I’m unsatisfied with it right

now, so I’d rather not post it.

I’m not claiming that either of these macros is actually a good idea;

I’m just trying to illustrate the possible.

It’s likely that quite a few of the features for ruby that get

requested could actually be implemented by macros. It’s probably

appropriate that most of these requests are rejected; we don’t really

need a lot of global changes to the language. However, if users can

write their own macros to scratch some of these itches, that’s a

better solution. They get the feature they want in just the program

that needs it, and the rest of us get a stable, predictable language

without a lot of weird new features in all other ruby programs.