Macros in Ruby

  • Thread starter George Moschovitis
  • Start date
G

George Moschovitis

Hello everyone,

one of the features of the LISP family of languages that is missing from
Ruby are macros. I think they are useful on a lot of occasions so
I would like to see Ruby support macros in the future.

However i think it is quite easy to emulate some form of macro
functionality in Ruby. Here is some simple code:

macros.rb:
----------

$__macros__ = {}
$__required__ = {}

module Kernel

alias_method :eek:ld_require, :require
def require(path)
return if $__required__[path]

source = open(path) { |f|
f.sysread(f.stat().size())
}

# parse macro
source.gsub!(/defmacro\s*\/(.*?)\/\s(.*?)endmacro/m) {
$__macros__[Regexp.new($1)] = $2 ; ""
}

# expand macros
$__macros__.each { |match, replace|
source.gsub!(match, replace)
}

$__required__[path] = true

eval(source)
end

end

require "test1.rb"
require "test2.rb"


test1.rb:
---------

defmacro /my_macro\((.*)\)/
begin
my_function(\1)
rescue => ex
puts ex
end
endmacro

# php style foreach
defmacro /foreach\s*\((.*)\s*as(.*)\)/
for \2 in \1
endmacro

def my_function(str)
puts str
end

class TestClass
def another_test
words = %w{ simple test }
foreach(words as word)
puts k
end
end
end


test2.rb:
---------

value = "Hello World!"
my_macro(value)

numbers = [1, 2, 3, 4]

foreach (numbers as i)
puts i
end

a = TestClass.new
a.another_test()



Once again i was suprised to find out that Ruby is so powerful!

However I would like to hear your opinion on this code. Is there any
problem I have overlooked? Is there a better implementation? Could this
be better designed?

If anyone finds it usefull I could create a small package with
doc+examples and upload it to RAA.


have fun,
George Moschovitis
 
J

jim

* George Moschovitis said:
Hello everyone,

one of the features of the LISP family of languages that is missing from
Ruby are macros. I think they are useful on a lot of occasions so
I would like to see Ruby support macros in the future.

However i think it is quite easy to emulate some form of macro
functionality in Ruby. Here is some simple code:

I've heard Matz say that Ruby will not support macros.
They are too easily abused and can mutate the language.
Besides, you can achieve the same powerful affect using
blocks.

Jim
 
J

Joel VanderWerf

George said:
Hello everyone,

one of the features of the LISP family of languages that is missing from
Ruby are macros. I think they are useful on a lot of occasions so
I would like to see Ruby support macros in the future.

What you have defined are more like C macros, rather than LISP macros,
which are hygienic (they operate at the level of syntactic elements,
rather than characters).
 
G

George Moschovitis

I've heard Matz say that Ruby will not support macros.
They are too easily abused and can mutate the language.

I 've heard that, but macros ARE usefull. Take a look
at www.paulgraham.com for example.
Besides, you can achieve the same powerful affect using
blocks.

How can i use blocks to emulate macros ? I cannot understand
that.

George
 
G

George Moschovitis

Joel said:
What you have defined are more like C macros, rather than LISP macros,
which are hygienic (they operate at the level of syntactic elements,
rather than characters).

You are of course right. But you can do still usefull things. And I
think it can be improved. Any ideas are welcome. Even better would be
support for macros in Ruby 1.9 :)

George.
 
J

James Britt

Joel said:
What you have defined are more like C macros, rather than LISP macros,
which are hygienic (they operate at the level of syntactic elements,
rather than characters).

Would Ruby macros, in the Lisp sense, have to manipulate the AST?

And a question (ideally) for Matz, but comments from anyone else are of
course welcome:

Would true macros in Ruby be more prone to abuse than they are in Lisp?

Is there something different about Ruby such that what (supposedly)
works to such acclaim in Lisp would be inappropriate in Ruby?


Thanks,



James
 
L

Lennon Day-Reynolds

Is there something different about Ruby such that what (supposedly)
works to such acclaim in Lisp would be inappropriate in Ruby?

A: S-expressions, or the lack thereof in Ruby. Lisp code can always be
manipulated as a simple tree of cons cells; Ruby code, even if a
high-level programmatic interface existed to the AST, has a much
richer syntax, and therefore more complex structure.

Also, as has been mentioned, Ruby largely replaces macros with blocks
-- they allow delayed evaluation, specialized iteration and
pre/post-condition checks, etc. Many of the common macros you'll see
in Lisp are things like this:

(with-open-file "foo.dat" my-file (case (read-char my-file) ...))

[my apologies for the weird function names; it's been a couple of
years since I was actively developing any Lisp code]

Obviously, that example is quite handily supported by the more
Ruby-esque example below:

open("foo.dat") {|my_file| case my_file.getc ...}

Personally, I think macros could be an interesting tool for very
special cases, but they're not an essential feature for 90% of
developers. Now, exposing the AST with a high-level API could have
other benefits: code analysis and optimization, JIT compilers,
documentation generators, etc.

Lennon
 
P

Phil Tomson

Hello everyone,

one of the features of the LISP family of languages that is missing from
Ruby are macros. I think they are useful on a lot of occasions so
I would like to see Ruby support macros in the future.
Once again i was suprised to find out that Ruby is so powerful!

However I would like to hear your opinion on this code. Is there any
problem I have overlooked? Is there a better implementation? Could this
be better designed?

If anyone finds it usefull I could create a small package with
doc+examples and upload it to RAA.


have fun,
George Moschovitis

I've never used Lisp so I'm not too familiar with Lisp macros and I know
that Matz isn't too fond of macros. However, I can't help feeling that
you've created something quite cool here and I'd certainly like to see
more.



Please do post something on the RAA! Maybe you could put it up on
rubyforge?

Phil
 
P

Phil Tomson

I've heard Matz say that Ruby will not support macros.

True, Matz has said that. But if we can do something Macro-like with a
module (as George has shown) then it's optional.
They are too easily abused and can mutate the language.
Besides, you can achieve the same powerful affect using
blocks.

Not quite. Notice that George introduced totally new 'syntax' using his
'macros' - you can't do that with blocks.


Phil
 
P

Phil Tomson

Personally, I think macros could be an interesting tool for very
special cases, but they're not an essential feature for 90% of
developers. Now, exposing the AST with a high-level API could have
other benefits: code analysis and optimization, JIT compilers,
documentation generators, etc.

....and code translators, and lots of other cool things.

Yes, this would be great to have.

Phil
 
M

Mauricio Fernández

I've heard Matz say that Ruby will not support macros.
They are too easily abused and can mutate the language.
Besides, you can achieve the same powerful affect using
blocks.

I've sometimes found myself wanting macros for things that *cannot* be
done with blocks currently; however, in most cases they could have been
accomplished easily had the often proposed extensions to Binding and
Kernel#caller been implemented.
 
P

Phil Tomson

Hello everyone,

one of the features of the LISP family of languages that is missing from
Ruby are macros. I think they are useful on a lot of occasions so
I would like to see Ruby support macros in the future.

However i think it is quite easy to emulate some form of macro
functionality in Ruby. Here is some simple code:

macros.rb:
----------

$__macros__ = {}
$__required__ = {}

module Kernel

alias_method :eek:ld_require, :require
def require(path)
return if $__required__[path]

source = open(path) { |f|
f.sysread(f.stat().size())
}

# parse macro
source.gsub!(/defmacro\s*\/(.*?)\/\s(.*?)endmacro/m) {
$__macros__[Regexp.new($1)] = $2 ; ""
}

# expand macros
$__macros__.each { |match, replace|
source.gsub!(match, replace)
}

$__required__[path] = true

eval(source)
end

end

require "test1.rb"
require "test2.rb"


test1.rb:
---------

defmacro /my_macro\((.*)\)/
begin
my_function(\1)
rescue => ex
puts ex
end
endmacro

# php style foreach
defmacro /foreach\s*\((.*)\s*as(.*)\)/
for \2 in \1
endmacro

def my_function(str)
puts str
end

class TestClass
def another_test
words = %w{ simple test }
foreach(words as word)
puts k
end
end
end


test2.rb:
---------

value = "Hello World!"
my_macro(value)

numbers = [1, 2, 3, 4]

foreach (numbers as i)
puts i
end

a = TestClass.new
a.another_test()



Once again i was suprised to find out that Ruby is so powerful!

However I would like to hear your opinion on this code. Is there any
problem I have overlooked? Is there a better implementation? Could this
be better designed?

Another comment: maybe instead of redefining 'require' you should just
have another method on kernel, something like 'load_macro' or
'require_macro'? This would make it clear that you are loading code which
contains macros. Perhaps even the file extension should be different for
these files to avoid confusion (*.rbm?).

Also, right now you can only define these macros in files that get
required, maybe you should also allow for the definition of macros in
strings or here-docs?

Phil
 
P

Phil Tomson

I've sometimes found myself wanting macros for things that *cannot* be
done with blocks currently; however, in most cases they could have been
accomplished easily had the often proposed extensions to Binding and
Kernel#caller been implemented.
Can you give some examples?

Phil
 
L

Lothar Scholz

Hello jim,

jfo> I've heard Matz say that Ruby will not support macros.

And i hope that he never changes his opinion at this topic.

jfo> They are too easily abused and can mutate the language.

They easily make code much more unreadable. I think that languages
like D, C# etc now exactly why they don't want something like macros.

As said before lisp is different because of the general S-Expression
style of this language.

I remember an asian saying about thinks like this:

"Der Kluge fügt jeden Tag etwas hinzu,
Der Weise entfernt jeden Tag etwas".

Translated to english it is something like:

"The clever man adds something every day,
The wise man removes something every day".
 
D

David A. Black

Hi --

Would Ruby macros, in the Lisp sense, have to manipulate the AST?

And a question (ideally) for Matz, but comments from anyone else are of
course welcome:

Would true macros in Ruby be more prone to abuse than they are in Lisp?

Is there something different about Ruby such that what (supposedly)
works to such acclaim in Lisp would be inappropriate in Ruby?

I'm answering from not very much knowledge of Lisp, and I know the
meta-meta-meta people disagree :) but in the form this idea has
always been represented to me -- namely, as a way to introduce
essentially arbitrary syntax -- I've found it unappealing. I think
Ruby can be great for application language things, but the idea of
having {} or . mean new things doesn't do much for me.

I guess I prefer to think of Ruby as a language with a certain syntax
than as a kind of host environment for syntax definition. That's
partly because I like Ruby's syntax, and I think Matz is better at
designing syntax than most of us would be :) And also because the
thought of having to learn new syntax for, potentially, every program
is daunting.


David
 
L

Lothar Scholz

Hello Jesse,

JJ> Banning a feature because it can be misused is a pretty weak rationale.

Banning an often misused features because it does not fit in the style of a
language is a very good rationale.


Read the chapter "On language design and evolution" from
http://www2.inf.ethz.ch/~meyer/ongoing/etl/#table

Username: "Talkitover"
Password: "etl3"
Yes this is a public accessable page !
 
G

Gavin Sinclair

Banning a feature because it can be misused is a pretty weak rationale.
It might make sense for something like goto where you can provide more
structured alternatives that take care of most cases, but I don't think
macros fall into this case.
It's been said in this thread that Ruby doesn't need macros because it
has blocks but, this too, seems like a very weak argument. After all
Lisp is famous for its support for closures and Lisp programmers still
value the maro support in Lisp.

That itself is a weak argument. Elsewhere in the thread it was
mentioned that most uses of macros in LISP are to do what Ruby does
naturally with blocks. Your statement implies that Ruby blocks are
equivalent to LISP closures, which is untrue.
extend the syntax of the language to directly support your problem
domain. For example, if you work with finite-state machines you can
write a macro that allows you to declaratively define FSMs.

I bet it's easy to declaritively define FSMs in Ruby. But beyond
that, mere talk of "extending syntax" doesn't translate from LISP to
Ruby, as LISP doesn't have anything recognisable as programming
language syntax.
2) Macros allow you to evaluate arguments zero or more times whereas
functions and blocks normally eagerly evaluate arguments once.

Yes, nice idea. My very limited understanding is that this is
typically used to implement control structures commonly found in other
languages.
3) Macros are inlined so they may execute faster than a block.

That's just a speed issue, which Matz is addressing in other ways.
Of these benefits, blocks only address point two and that in a rather
clunky fashion.
Different only in that it's easier to implement macros in Lisp. But
more conventional languages, like Dylan, also support macros.

So it's an implementation issue. But when the difficulty of
implementation becomes large, the rationale for doing so must become
strong. I know that IANAL (I am being anal), but you have to
demonstrate some real benefit to Ruby, not benefit to LISP.

Cheers,
Gavin
 
J

James Britt

Jesse said:
Banning a feature because it can be misused is a pretty weak rationale.

I tend to agree on this, preferring an enabling language to a B&D
language. Ruby already gives folks plenty of tools for making code
unreadable. I think most people would use macros to do the opposite:
encapsulate some abstraction to make code cleaner.

David Alan Black mentioned macros as (possibly) a facility for
introducing arbitrary syntax. I'm no Lisper, but no examples I've seen
of Lisp macros suggest that this is what goes on. I do not think you
can use Lisp macros to get a Lisp interpreter to under truly arbitrary code.

But, even supposing one could, I believe the Darwinian forces in a
development community would prevent abuse of macros from becoming prevalent.
 
D

David A. Black

Hi --

David Alan Black mentioned macros as (possibly) a facility for
introducing arbitrary syntax. I'm no Lisper, but no examples I've seen
of Lisp macros suggest that this is what goes on. I do not think you
can use Lisp macros to get a Lisp interpreter to under truly arbitrary code.

My ignorance of Lisp macros is almost complete. Mainly I was going by
the (possibly completely wrong) impression that what people usually
mean by "having macros in Ruby" is allow on-the-fly redefinition of
syntax, including punctuation.


David
 
P

Phil Tomson

I tend to agree on this, preferring an enabling language to a B&D
language. Ruby already gives folks plenty of tools for making code
unreadable. I think most people would use macros to do the opposite:
encapsulate some abstraction to make code cleaner.

David Alan Black mentioned macros as (possibly) a facility for
introducing arbitrary syntax. I'm no Lisper, but no examples I've seen
of Lisp macros suggest that this is what goes on. I do not think you
can use Lisp macros to get a Lisp interpreter to under truly arbitrary code.

But, even supposing one could, I believe the Darwinian forces in a
development community would prevent abuse of macros from becoming prevalent.

Agreed.

While Matz may have 'banned' macros from the core language, that doesn't
mean that if someone comes up with a way to do LISP-like macros
implemented in an external module, (that could be downloaded
from the RAA for example) that Matz would ban the module. He probably
would never consider bundling it with the Ruby distribution, of course,
but that doesn't mean it would be 'banned'.

If such a module did ever become available those who would be interested
in such things would use it and those who were not interested would
ignore it.

It's kind of like how the static typing advocates occasionally come up
with some code for emulating static typing in Ruby (and you can probably
find some on the RAA) while most of us think that such things are
ill-advised, no one is stopping the advocates of such things from using
it.

Phil
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,900
Latest member
Nell636132

Latest Threads

Top