Macros in Ruby

In article <5d4c6124040802104572179afa@mail.gmail.com>,

···

Lennon Day-Reynolds <rcoder@gmail.com> wrote:

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

Hi --

In article <126117684609.20040802210655@scriptolutions.com>, Lothar

> Hello jim,
>
> >> 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.
>
> And i hope that he never changes his opinion at this topic.
>
> > They are too easily abused and can mutate the language.
>
> They easily make code much more unreadable.

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

I think it's more a question of a judgement being made (by Matz) that
it is very likely to be misused, and/or that even routine use of it
would take the language in directions he doesn't want to go.

In any case -- don't think of it as banning a feature, any more than
Beethoven "banned" the flute from his cello sonatas. Flute and cello
can go well together, but I wouldn't want to give up those pieces :slight_smile:

David

···

On Tue, 3 Aug 2004, Jesse Jones wrote:

Scholz <mailinglists@scriptolutions.com> wrote:

--
David A. Black
dblack@wobblini.net

Hello Jesse,

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 !

···

--
Best regards, emailto: scholz at scriptolutions dot com
Lothar Scholz http://www.ruby-ide.com
CTO Scriptolutions Ruby, PHP, Python IDE 's

In article <126117684609.20040802210655@scriptolutions.com>, Lothar

Hello jim,

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

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

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

They easily make code much more unreadable.

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.

From what I can tell macros offer three benefits: 1) They allow you to

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.

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

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

···

On Tuesday, August 3, 2004, 8:01:38 AM, Jesse wrote:

Scholz <mailinglists@scriptolutions.com> wrote:

Jesse Jones wrote:
  >

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.

matz@ruby-lang.org (Yukihiro Matsumoto) wrote in message news:<1091501355.227383.29355.nullmailer@picachu.netlab.jp>...

  * I admit disclosing abstract syntax tree to Ruby programs can open
    new possibilities. it's good for users at the moment, I guess.
    but it is very bad in a long run unless I design it perfectly.
    I'm sure I will change my mind in AST design and alas, here comes
    another big field of incompatibility.

is this really a problem?
IMO a developer accessing the AST (or just the parser)
should be aware of what he's doing and expect some breakage.
IIRC python has modules to access, the AST, the parser and the VM
internals, and this has proved quite ufeful even if python changed
much over time..

In article <1091501355.227383.29355.nullmailer@picachu.netlab.jp>,

Hi,

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

  * Lisp does not have syntax. They only have meta syntax
    (i.e. S-expression), Lisp macro do not change its (meta) syntax to
    rely on (I'm ignoring reader macro here). In comparison, Ruby
    does have syntax but no meta syntax, so that you can easily loose
    syntax to rely on by macros in Ruby. You will feel like a stranger
    when you see Ruby programs with a lot of macros. I don't think
    you feel same way when you see Lisp programs with macros.

I don't buy this argument at all. It's not like good Ruby programmers
would all of a sudden use macros to rewrite the Ruby grammar. Instead
they would augment the grammar with productions that make sense for
their applications.

For example, suppose you're writing a game and you have a lot of code
that selects behavior based on percentages (75% of hits are normal
blows, 20% are critical hits, and 5% kill your foe out-right for
example). Even in a language as expressive as Ruby this is a bit
awkward to write and not especially intentional. But with macros it can
be written very cleanly:

percent-case
   0.75 case
      normal_hit
   end

   0.20 case
      critical_hit
   end

   otherwise
      kill_foe
   end
end

  * macro system more than simplest one (e.g. C preprocessor macro) is
    VERY difficult to design and implement for languages with usual
    syntax. If you are curious, see Dylan's macro.

I've done this myself and I wouldn't call it very difficult. It's
certainly not trivial, but well within the capabilities of anyone who
can write a production compiler. "Extensible Language Implementation"
by Donovan Kolbly
(<http://www.cs.utexas.edu/ftp/pub/techreports/tr02-71.ps.gz&gt;\) is a
good PHD thesis on adding macros to conventional languages.

  * about 50% of macro usage in Lisp (extending syntax a bit) can
    accomplish using blocks in Ruby. we don't need inlining functions
    when we have other rooms for optimization, for example, C extensions.

I don't know Lisp well enough to know if that figure is in the
ballpark. I do find the thought of being able to extend the language to
match my problem instead of contorting my code to fit the language
compelling though.

  * I admit disclosing abstract syntax tree to Ruby programs can open
    new possibilities. it's good for users at the moment, I guess.
    but it is very bad in a long run unless I design it perfectly.
    I'm sure I will change my mind in AST design and alas, here comes
    another big field of incompatibility.

This is something I have been thinking about as well. One thing that
has held me back is that it requires all implementations to expose the
same AST if you want any sort of compatibility. This seems awfully icky
for any kind of complex language.

On the other hand, macros can play a role here: you can define a very
simple kernel language (lambda calculus with one or two extensions say)
and use macros to layer a more agreeable syntax on top of the kernel.
This way the AST exposed by clients is simple enough that it doesn't
overly constrain implementations.

Of course you don't absolutely need macros for this. You can instead do
these transforms within the front-end of the compiler. But macros are a
clean way to do them and offer the same power to developers.

  -- Jesse

···

Yukihiro Matsumoto <matz@ruby-lang.org> wrote:

In message "Re: Macros in Ruby" > on 04/08/03, James Britt <jamesUNDERBARb@neurogami.com> writes:

I'll add partial evaluation and refactoring to the list. This _would_
be interesting, yes.
  -CWS

···

On Tue, 3 Aug 2004 03:51:37 +0900, Phil Tomson <ptkwt@aracnet.com> wrote:

In article <5d4c6124040802104572179afa@mail.gmail.com>,
Lennon Day-Reynolds <rcoder@gmail.com> wrote:
>
>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

Hi --

···

On Tue, 3 Aug 2004, James Britt wrote:

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

--
David A. Black
dblack@wobblini.net

In article <410EC9A6.5010107@neurogami.com>,

Jesse Jones wrote:
>

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.

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

···

James Britt <jamesUNDERBARb@neurogami.com> wrote:

In article <193-117860805.20040803085837@soyabean.com.au>, Gavin

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

Yes, that was asserted and one example was offered to support it. It
may even be true that most Lisp macros can be done more or less
naturally using anonymous functions or closures. But I suspect that the
code that uses macros is more intentional. And, of course, the more
complex macros especially the domain specific ones will often be
expressed much more cleanly using macros.

Your statement implies that Ruby blocks are
equivalent to LISP closures, which is untrue.

As far as I can tell Lisp lambda functions (ie closures) do everything
that Ruby blocks and proc objects do but with cleaner semantics and
fewer artifical distinctions.

>>From what I can tell macros offer three benefits: 1) They allow you to
> 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.

I'm not so sure, I experimented with defining FSMs in a dynamic
language with good support for anonymous functions/closures and the
macro based solution was a whole lot nicer to use.

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.

Like I said it's perfectly feasible to have a decent hygienic macro
system in a language with conventional 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.

Yeah, that's one common application.

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

True, but speed is important. And in many OO languages inlined code can
do things that functions cannot. For example, in a lot of OO languages
you can't write a swap function, but a swap macro is trivial.

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

I actually come at this from a Dylan POV, not from a Lisp one and Dylan
has a conventional syntax. My concern is that most languages are
insufficiently expressive and macros seem like a great way to extend
the expressiveness of a language.

Of course this assumes a decent macro system: one where macros respect
scope, are integrated into the grammar so they don't run wild like C
macros, and are hygienic so their meaning isn't context dependant.

  -- Jesse

···

Sinclair <gsinclair@soyabean.com.au> wrote:

On Tuesday, August 3, 2004, 8:01:38 AM, Jesse wrote:

Jesse Jones wrote:

For example, suppose you're writing a game and you have a lot of code
that selects behavior based on percentages (75% of hits are normal
blows, 20% are critical hits, and 5% kill your foe out-right for
example). Even in a language as expressive as Ruby this is a bit
awkward to write and not especially intentional. But with macros it can
be written very cleanly:

percent-case
   0.75 case
      normal_hit
   end

   0.20 case
      critical_hit
   end

   otherwise
      kill_foe
   end
end

It's not really that hard to do something like this in ruby, but it is a little less efficient than it would be with macros.

   class PercentCase
     def initialize block
       @cases =
       instance_eval(&block)
     end

     def choose
       n = rand(100)
       @cases.each do |percent, block|
         n -= percent
         return block.call if n < 0
       end
       otherwise.call
     end

     def chance percent, &block
       @cases << [percent, block]
     end

     def otherwise(&block)
       @otherwise = block
     end
   end

   def percent_case(&block)
     PercentCase.new(block).choose
   end

   10.times do
     percent_case do
       chance 75 do
         puts "normal_hit"
       end

       chance 20 do
         puts "critical_hit"
       end

       otherwise do
         puts "kill_foe"
       end
     end
   end

To make it more efficient, you could keep the PercentCase object around and reuse it each time.

Jesse Jones wrote:

For example, suppose you're writing a game and you have a lot of code
that selects behavior based on percentages (75% of hits are normal
blows, 20% are critical hits, and 5% kill your foe out-right for
example). Even in a language as expressive as Ruby this is a bit
awkward to write and not especially intentional. But with macros it can
be written very cleanly:

percent-case
   0.75 case
      normal_hit
   end

   0.20 case
      critical_hit
   end

   otherwise
      kill_foe
   end
end

I'm sorry to say it, but I think you just proved Matz' point there. I look at that, and it doesn't look like Ruby to me. Even with your explanation, I don't understand how it works, or how to modify it. Is "case" in your code the normal ruby "case" statement? It doesn't look like it.

The one place I've really missed macros is in debugging. I'm really used to C macros that are really simple to type 'dbg(val);' but can produce detailed valuable output:

foo.c:132 do_it() val => 23

There are hackish ways to get something approaching this in Ruby, mainly by twiddling with the 'caller' array, and using symbols to grab the name of the symbol along with its value. That feels really hackish to me. I'd feel slightly better about it if callers returned an array of "SourceLine" objects, or something, and you could get the method name as well. But it's still not quite up there with the ease of use of a C macro.

On the other hand, aside for this and a few other isolated cases, I *hate* C macros, and *hate* the C preprocessor. It means that when you're writing .c and .h files, you're not actually writing C code, you're writing C/C-preprocessor code, and depending on who has touched the codebase, you never quite know what's what.

I don't know if there's a middle ground, but I certainly lean towards avoiding macros.

Ben

Jesse Jones wrote:

percent-case
   0.75 case
      normal_hit
   end

   0.20 case
      critical_hit
   end

   otherwise
      kill_foe
   end
end

I spent about 10 minutes coding this in Ruby (and it turned out to be very similar to Joel VanderWerf's version that he posted). I'm curious what a macro version would be like. What would the implementation of percent_case look like in Lisp or Dylan macros?

···

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

I'm sorry, but I don't see how this is more readable than:

  case hit_roll
    when (0.0 .. 0.75)
      normal_hit
    when (0.75 .. 0.95)
      critical_hit
    else
      kill_foe
    end
  end

Yes, I've switched the percentages to Ranges, but there's no reason
that you couldn't build a similar mechanism into one of your own
classes:

  class Attacker
    def initialize
      @total = 0
      @results = {}
      yield self if block_given?
    end

    def add_result(percentage, &block)
      if (percentage > 100) or (@total + percentage > 100)
        raise ArgumentError
      end

      percentage_range = (@total .. (@total + percentage))
      @results[percentage_range] = block
      @total += percentage
    end

    def hit(percentage)
      if percentage.kind_of?(Float)
        percentage = (percentage * 100).to_i
      end
      res = nil
      @results.each_key do |rr|
        if rr === percentage
          res = @results[rr].call(percentage)
          break
        end
      end
      res
    end
  end

  hero = Attacker.new do |hh|
    hh.add_result(75) { |p| puts "Normal hit with #{p} roll." }
    hh.add_result(20) { |p| puts "Critical hit with #{p} roll." }
    hh.add_result(5) { |p| puts "Deadly hit with #{p} roll." }
  end

  (0 ... 20).each { hero.hit(rand) }

    # Result:
  Normal hit with 60 roll.
  Normal hit with 5 roll.
  Normal hit with 75 roll.
  Normal hit with 74 roll.
  Normal hit with 43 roll.
  Normal hit with 10 roll.
  Normal hit with 8 roll.
  Normal hit with 21 roll.
  Normal hit with 31 roll.
  Deadly hit with 99 roll.
  Normal hit with 50 roll.
  Normal hit with 47 roll.
  Normal hit with 48 roll.
  Normal hit with 72 roll.
  Normal hit with 3 roll.
  Critical hit with 82 roll.
  Normal hit with 48 roll.
  Critical hit with 86 roll.
  Critical hit with 76 roll.
  Normal hit with 3 roll.

-austin

···

On Wed, 4 Aug 2004 08:26:32 +0900, Jesse Jones <jesjones@mindspring.com> wrote:

In article <1091501355.227383.29355.nullmailer@picachu.netlab.jp> ,
Yukihiro Matsumoto <matz@ruby-lang.org> wrote:

In message "Re: Macros in Ruby" >> on 04/08/03, James Britt <jamesUNDERBARb@neurogami.com> writes:

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

* Lisp does not have syntax. They only have meta syntax (i.e.
  S-expression), Lisp macro do not change its (meta) syntax to
  rely on (I'm ignoring reader macro here). In comparison, Ruby
  does have syntax but no meta syntax, so that you can easily
  loose syntax to rely on by macros in Ruby. You will feel like a
  stranger when you see Ruby programs with a lot of macros. I
  don't think you feel same way when you see Lisp programs with
  macros.

I don't buy this argument at all. It's not like good Ruby
programmers would all of a sudden use macros to rewrite the Ruby
grammar. Instead they would augment the grammar with productions
that make sense for their applications.

For example, suppose you're writing a game and you have a lot of
code that selects behavior based on percentages (75% of hits are
normal blows, 20% are critical hits, and 5% kill your foe
out-right for example). Even in a language as expressive as Ruby
this is a bit awkward to write and not especially intentional. But
with macros it can be written very cleanly:

percent-case
  0.75 case
    normal_hit
  end

  0.20 case
    critical_hit
  end

  otherwise
    kill_foe
  end
end

--
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca

"David A. Black" <dblack@wobblini.net> writes:

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.

Lisp does allow you to completely redefine the syntax, but through
features that aren't usually included in the typical definition of
``Lisp macros'' -- namely, the ability to define reader macros, which
basically lets you hook into the lexer and do whatever you want.

Lisp syntax essentially consists of a bunch of literals (integer
literals, string literals, symbols, etc) and ``forms.'' A form is
a list, and looks like (foo bar baz). In typical circumstances, the
evaluation of a form is initially dispatched on the head of the list.

If the head is a function name -- i.e., either a symbol whose value is a
function, or a LAMBDA form -- then the corresponding function is called
with the results of evaluation of every other item in the list.

If the head names a macro, the tail of the list is applied to the macro
without evaluating the arguments. The macro is like a regular Lisp
function, but what it's returned isn't the value of the form -- first,
the result is evaluated; the macro returns an expression, not a value.

Lisp doesn't make that annoying, since Lisp syntax consists solely of
Lisp values. (+ 1 2) is just a list of the symbol +, the number 1, and
the number 2.

If Lisp macros were implemented in Ruby, they wouldn't necessarily let
you change the syntax to your whims. Your macros would need to conform
to the standard method call syntax, for example.

It's true that many Lisp macros are merely kludges to avoid typing
`lambda', and Ruby's blocks address that nicely. You can even specify
some data declaratively with blocks. But Lisp macros are definitely
more powerful -- they can do arbitrary introspection and manipulation of
their arguments.

Also, I'm not very coherent at this time of night.

  mikael

···

On Tue, 3 Aug 2004, James Britt wrote:

Phil Tomson wrote:

In article <410EC9A6.5010107@neurogami.com>,

...

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.

Oh, nice move. Associating Lisp-style macros with static typing.

I curse you, Phil Tomson!

James

:slight_smile:

···

James Britt <jamesUNDERBARb@neurogami.com> wrote:

In article <cemm7i0126g@enews2.newsguy.com>, Phil Tomson

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

This seems hard to do well. A good macro system really needs to be
integrated into the language. If not, you wind up with abortions like
the C/C++ macro system which is both dangerous to use and
under-powered.

It might be possible to do with an open compiler (ie one that provided
hooks to allow third-party components to affect its behavior). I tend
to be a bit suspicious of open compilers though. They seem to either
lock users into particular implementations or significantly constrain
compiler implementations. I'll have to think about it some more, but it
seems like adding macros wouldn't be all that much more difficult to
implement than opening the parser up.

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.

I don't see the connection. Static typing runs counter to the whole
concept of typing in Ruby. Macros, on the other hand, are a completely
orthogonal concept and, if anything, fit well into the Ruby philosophy
of providing a flexible and expressive language.

  -- Jesse

···

<ptkwt@aracnet.com> wrote:

Jesse Jones wrote:

Your statement implies that Ruby blocks are
equivalent to LISP closures, which is untrue.

As far as I can tell Lisp lambda functions (ie closures) do everything
that Ruby blocks and proc objects do but with cleaner semantics and
fewer artifical distinctions.

What are some of the differences? I've been away from lisp for a while....

From what I can tell macros offer three benefits: 1) They allow you to

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.

I'm not so sure, I experimented with defining FSMs in a dynamic
language with good support for anonymous functions/closures and the
macro based solution was a whole lot nicer to use.

I've found ruby blocks to be fairly good for defining declarative constructs. For example, I based the syntax for a declarative language for hybrid automata (~ FSMs + ODEs) on this technique. Here's a sample:

class Vehicle < Component
   state :SpeedUp, :SlowDown

   default do
     self.x = 0
     self.xDot = 20
     start SlowDown
   end

   setup do
     @timer = create(Timer)
   end

   flow SpeedUp, SlowDown do
     diff "xDot' = xDDot"
     diff "x' = xDot"
   end

   flow SpeedUp do
     alg "xDDot = 3"
   end

   flow SlowDown do
     alg "xDDot = -3"
   end

   transition SpeedUp => SlowDown, SlowDown => SpeedUp do
     guard {@timer.tick}
     action {puts "Tick!"}
   end
end

(The parsing of all these blocks--using instance_eval plus manual parsing of the differential/algebraic equations--happens at load time, then there is a compile stage which generates a dynamic lib and loads it, and at run time it's mostly native code executing, with an occasional call to a ruby proc or method.)

The one respect in which ruby was less than perfectly suitable to this syntactical problem is having to use "self.x = ..." all the time, but there are good reasons for that in the bigger picture.

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.

Yeah, that's one common application.

Hm? Blocks can be used to defer evaluation, using the &block or Proc.new constructs. Or do you mean something else?

I actually come at this from a Dylan POV, not from a Lisp one and Dylan

I still wish Dylan hadn't been left to wither...

The one place I've really missed macros is in debugging. I'm really
used to C macros that are really simple to type 'dbg(val);' but can
produce detailed valuable output:

foo.c:132 do_it() val => 23

This looks more like logging than debugging. C macros typically make
debugging more difficult, not less, because most debuggers don't let you
"step into" a macro like you can with a function.

There are hackish ways to get something approaching this in Ruby, mainly
by twiddling with the 'caller' array, and using symbols to grab the name
of the symbol along with its value. That feels really hackish to me.
I'd feel slightly better about it if callers returned an array of
"SourceLine" objects, or something, and you could get the method name as
well. But it's still not quite up there with the ease of use of a C macro.

The caller() method certainly has its problems. The API could be easier
to use, but that doesn't make C macros better. You are comparing the
design of two APIs (the Ruby API and the C preprocessor API), not macros
versus no-macros.

I don't know if there's a middle ground, but I certainly lean towards
avoiding macros.

What experience do you have with lisp macros or other non-C-preprocessor
macros?

Paul

···

On Wed, Aug 04, 2004 at 09:15:25AM +0900, Ben Giddings wrote: