From: Caleb Clausen Date: 2008-11-03T07:40:19+09:00 Subject: Re: [ANN] RubyMacros 0.1.0 Released On 10/26/08, ara.t.howard wrote: > > On Oct 24, 2008, at 2:23 PM, Caleb Clausen wrote: > >> == Benefits: >> * Powerful and easy metaprogramming >> * Create better DSLs >> * Manipulate syntax trees to suit yourself >> * Access local variables and other caller context unavailable to >> methods >> * Macros as inline methods: should be slightly faster than >> 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.