James M. Lawrence wrote:
> On Tue, Feb 24, 2009 at 12:29 AM, Yehuda Katz <wycats / gmail.com>
wrote:
>> Matz has repeatedly and explicitly expressed that he did not want
Ruby to
>> have macros. That might have changed, but I believe he repeated it
recently
>> at LoneStar RubyConf. I believe Matz has argued that macros are too
complex
>> for the average programmer...

I would argue that the workarounds that are currently used to get 
macro-like functionality are even more complex.

> I have been specifically talking about a simple string-based macro
> system which does not modify Ruby syntax.  It is not susceptible to
> all the criticism Matz has given toward macros in general.  I should
> have been more explicit about this, but my post was already too
long.

I'm curious: have you ever looked at the way macro-like 
functionality for a dynamic language with rich, complex, 
irregular syntax is implemented via compile-time metaprogramming 
in Converge (<http://ConvergePL.Org/>)?

I quite like it, if only because I *am* an average programmer and 
I *don't* find it too complex.

The basic idea is that you use the actual *concrete* syntax of 
the programming language to build abstract syntax trees. And 
where that doesn't work, you can alternatively build ASTs with an 
abstract, generic, compiler API. The trick is that in both cases, 
you don't have to (and should not!) know what the actual AST 
looks like. All you know, is, that when you use this "magic" 
syntax or call this method, you get back some opaque object which 
you can compose with other ASTs (again by calling the abstract 
compiler API) or feed back into the compiler.

Neither the internal compiler/interpreter/parser implementation 
nor the internal AST is exposed to the programmer.

Here's how it works.

Quasi-quotes are used to turn concrete syntax into ASTs. Converge 
uses [| and |] as delimiters. For example, this:

	def foo
	  return [| 1+1 |]
	end

is equivalent to

	def foo
	  return [:call, [:lit, 1], :+, [:arglist, [:lit, 1]]]
	end

except that the latter requires intimate knowledge of the AST.

Splices are used to splice AST fragments back into the AST. 
Converge's syntax for splices is $< and >

	def bar
	  return $<foo> # Evaluates foo and splices its value in the
AST
	end

The third piece of the puzzle is called inserts. Those are used 
to insert expressions into quasi-quotes. They use ${ and }

This example is stolen directly from the Converge documentation, 
which in turn takes it from the Template Haskell whitepaper:

	def expand_power(n, x)
	  if n == 0
	    return [| 1 |]
	  else
	    return [| $c{x} * $c{expand_power(n - 1, x)} |]
	    # The 'c' modifier means "capture" or IOW "unhygienic", so

	    # we can access the 'n' and 'x' variables.
	    # By default everything is hygienic.
	  end
	end

	def mk_power(n)
	  return [| -> (x) { return $c{expand_power(n, [| x |])} } |]
	end

	power3 = $<mk_power(3)>

This is equivalent to

	power3 = -> (x) { return x * x * x * 1 }

This is obviously not a very compelling example.

Compared to Lisp, the main difference is that in Lisp, *macro 
definitions* are special, but *macro calls* look like ordinary 
function calls, whereas in Converge *macro definitions* are just 
ordinary methods, but *macro calls* have special syntax.

BTW: I think this syntax is quite scary and ugly ... and that is 
actually a good thing! Unlike Lisp, macros *should* be scary 
looking in Ruby.

> From 2004:
> 
> http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/108143
> 
> Matz wrote on Tue, 3 Aug 2004
>> * 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.
> Agreed, and this is not relevant to my macro proposal.  I said both
> the initial code and the substituted code must be valid Ruby syntax.
> It will always look like Ruby, though some of it will look like Ruby
> inside a string.

... or inside $i||y [hara<ters.

>> * 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.
> Agreed, and this is not my proposal.  String substitutions are easy,
> yet hugely better than a C preprocessor since arbitrary code can run
> before producing the final code string.  I showed an example of a
> recursive macro which generates Fibonacci numbers at parse time.

This is an alternative implementation of computing the 30th 
Fibonacci number at compile-time, Converge-style:

	def fib(n)
	  if n == 0
	    return 0
	  elsif n == 1
	    return 1
	  else
	    return fib(n - 1) + fib(n - 2)
	  end
	end

	fib30 = $<CEI.lift(fib(30))>

This is an example of the standard compiler API (Compiler 
External Interface - CEI): since a splice can only take an AST, 
but fib(30) returns an Integer, we need to "lift" that Integer to 
an AST. Note again that the lift method neither exposes internal 
compiler implementation details nor AST implementation details.

>> * 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.
> Agreed.  I said that implementations should not disclose their AST.

And with this, they don't need to.

jwm