Macros in Ruby

  • Follow


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 :old_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

-- 
www.navel.gr
www.joy.gr
0
Reply gm551 (80) 8/2/2004 9:14:13 AM

* George Moschovitis <gm@navel.gr> [2004-08-02 18:16:35 +0900]:

> 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


0
Reply jim5624 (470) 8/2/2004 3:45:02 PM


George Moschovitis wrote:
> 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).


0
Reply vjoel (2600) 8/2/2004 4:50:23 PM

> 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

-- 
www.navel.gr
www.joy.gr
0
Reply gm551 (80) 8/2/2004 5:18:09 PM

Joel VanderWerf wrote:
> 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.


-- 
www.navel.gr
www.joy.gr
0
Reply gm551 (80) 8/2/2004 5:21:09 PM

Joel VanderWerf wrote:
> George Moschovitis wrote:
> 
>> 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).

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



0
Reply jamesUNDERBARb1 (671) 8/2/2004 5:29:34 PM

On Tue, 3 Aug 2004 02:29:34 +0900, James Britt
<jamesunderbarb@neurogami.com> wrote:
> 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


0
Reply rcoder (210) 8/2/2004 5:45:10 PM

In article <cel0la$2sto$1@ulysses.noc.ntua.gr>,
George Moschovitis  <gm@navel.gr> wrote:
>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.
>
<code snipped>


>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
0
Reply ptkwt (713) 8/2/2004 6:14:00 PM

In article <20040802154500.GB67163@freeze.org>,  <jim@freeze.org> wrote:
>* George Moschovitis <gm@navel.gr> [2004-08-02 18:16:35 +0900]:
>
>> 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.

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
0
Reply ptkwt (713) 8/2/2004 6:17:12 PM

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
0
Reply ptkwt (713) 8/2/2004 6:22:24 PM

On Tue, Aug 03, 2004 at 12:45:02AM +0900, jim@freeze.org wrote:
> 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.

-- 
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com



0
Reply batsman.geo (482) 8/2/2004 6:27:57 PM

In article <cel0la$2sto$1@ulysses.noc.ntua.gr>,
George Moschovitis  <gm@navel.gr> wrote:
>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 :old_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
0
Reply ptkwt (713) 8/2/2004 6:28:57 PM

In article <20040802182747.GA7661@student.ei.uni-stuttgart.de>,
Mauricio Fern�ndez  <batsman.geo@yahoo.com> wrote:
>On Tue, Aug 03, 2004 at 12:45:02AM +0900, jim@freeze.org wrote:
>> 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.
>
Can you give some examples?

Phil

0
Reply ptkwt (713) 8/2/2004 6:46:08 PM

Hello jim,

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

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".
 
 
-- 
 Best regards,                        emailto: scholz at scriptolutions dot com
 Lothar Scholz                        http://www.ruby-ide.com
 CTO Scriptolutions                   Ruby, PHP, Python IDE 's
 



0
Reply mailinglists1 (626) 8/2/2004 7:07:26 PM

In article <126117684609.20040802210655@scriptolutions.com>, Lothar
Scholz <mailinglists@scriptolutions.com> wrote:

> Hello jim,
> 
> >> However i think it is quite easy to emulate some form of macro
> >> functionality in Ruby. Here is some simple code:
> 
> 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.

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.

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. 2) Macros
allow you to evaluate arguments zero or more times whereas functions
and blocks normally eagerly evaluate arguments once. 3) Macros are
inlined so they may execute faster than a block.

Of these benefits, blocks only address point two and that in a rather
clunky fashion.

> I think that languages
> like D, C# etc now exactly why they don't want something like macros.

How so? I don't know these languages well, but I don't see that they've
done anything to address the problems macros were designed to solve.
They've merely omitted features like meta-programming, dynamic typing,
full multiple inheritance, etc where they thought the cost out-weighed
the benefits.

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

  -- Jesse
0
Reply jesjones (15) 8/2/2004 9:57:26 PM

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

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


0
Reply DocBoobenstein (81) 8/2/2004 10:40:53 PM

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 !


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



0
Reply mailinglists1 (626) 8/2/2004 10:49:58 PM

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

> In article <126117684609.20040802210655@scriptolutions.com>, Lothar
> Scholz <mailinglists@scriptolutions.com> wrote:

>> Hello jim,
>> 
>> >> However i think it is quite easy to emulate some form of macro
>> >> functionality in Ruby. Here is some simple code:
>> 
>> 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.

> 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




0
Reply gsinclair1 (862) 8/2/2004 11:00:47 PM

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.



0
Reply jamesUNDERBARb1 (671) 8/2/2004 11:09:22 PM

In article <410EC9A6.5010107@neurogami.com>,
James Britt  <jamesUNDERBARb@neurogami.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.
>

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
0
Reply ptkwt (713) 8/3/2004 12:28:34 AM

Hi,

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.

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

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

							matz.


0
Reply matz (1855) 8/3/2004 3:00:32 AM

Phil Tomson wrote:
> In article <410EC9A6.5010107@neurogami.com>,
> James Britt  <jamesUNDERBARb@neurogami.com> wrote:
> 
...

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

:)



0
Reply jamesUNDERBARb1 (671) 8/3/2004 4:45:38 AM

In article <410F187F.1070602@neurogami.com>,
James Britt  <jamesUNDERBARb@neurogami.com> wrote:
>Phil Tomson wrote:
>> In article <410EC9A6.5010107@neurogami.com>,
>> James Britt  <jamesUNDERBARb@neurogami.com> wrote:
>> 
>..
>
>>>
>>>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!
>

Ohhh no!! Another 7 years! ;-)

Actually, I find the idea of Lisp-style macros interesting, while I find 
the idea of static typing in Ruby just plain bad... but YMMV.

Phil
0
Reply ptkwt (713) 8/3/2004 6:10:16 AM

"George Moschovitis" <gm@navel.gr> schrieb im Newsbeitrag
news:celt6a$1icq$1@ulysses.noc.ntua.gr...
> Joel VanderWerf wrote:
> > 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 :)

-1

    robert

0
Reply bob.news (3805) 8/3/2004 8:23:24 AM

> Another comment: maybe instead of redefining 'require' you should just 
....
> strings or here-docs?

Hello Phil,

thanks for your comments. I 'll work on the code a bit and post a new
version. I 'll try to include your requests. I agree with you that
macros are usefull. For example i use them in a coll localization 
scheme, or even for loging (using this simple macro implementation,
I can fully strip the logging/profiling code, much better than using
a block, and more flexible than using AOP techinques).

Anyway it would be nice to hear more ideas, comments!

regards,
George Moschovitis


-- 
www.navel.gr
www.joy.gr
0
Reply gm551 (80) 8/3/2004 9:00:42 AM

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..
0
Reply surrender_it1 (15) 8/3/2004 9:38:31 AM

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

> Hi,
> 
> 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

>   * 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>) 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
0
Reply jesjones (15) 8/3/2004 11:24:04 PM

In article <193-117860805.20040803085837@soyabean.com.au>, Gavin
Sinclair <gsinclair@soyabean.com.au> wrote:

> On Tuesday, August 3, 2004, 8:01:38 AM, Jesse wrote:
> 
> > 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
0
Reply jesjones (15) 8/3/2004 11:42:35 PM

In article <cemm7i0126g@enews2.newsguy.com>, Phil Tomson
<ptkwt@aracnet.com> wrote:

> 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
0
Reply jesjones (15) 8/3/2004 11:59:09 PM

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.


0
Reply vjoel (2600) 8/4/2004 12:12:41 AM

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


0
Reply bg-rubytalk (315) 8/4/2004 12:15:25 AM

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


0
Reply vjoel (2600) 8/4/2004 12:38:42 AM

In article <030820041658374069%jesjones@mindspring.com>,
Jesse Jones  <jesjones@mindspring.com> wrote:
>In article <cemm7i0126g@enews2.newsguy.com>, Phil Tomson
><ptkwt@aracnet.com> wrote:
>
>> 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.

Read my later post.  I was only using that debate as an example...

There is a camp of folks who think that Ruby needs to emulate 
static-typing in some cases.  There is another camp that thinks this is a 
bad idea.  Similarly, there is a camp that says that macros would be 
useful (or at least interesting to explore - that's kind of where I'm at 
since I've never used LISP macros) and there is another camp (which Matz 
is in) that says that macros are just plain bad.  Since Matz is in the 
latter camp you're not likely to see macros included in Ruby 
'out-of-the-box', but if you can some up with an addon to do macros (if 
it's possible)...

I wasn't trying to tie static typing and macros together.

Phil
0
Reply ptkwt (713) 8/4/2004 12:47:18 AM

On Wed, Aug 04, 2004 at 09:15:25AM +0900, Ben Giddings wrote:
> 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



0
Reply pbrannan (331) 8/4/2004 2:59:41 AM

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)


0
Reply jim9733 (726) 8/4/2004 4:45:21 AM

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

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
-- 
Austin Ziegler * halostatue@gmail.com
               * Alternate: austin@halostatue.ca


0
Reply halostatue (1713) 8/4/2004 5:00:37 AM

In article <41102A98.806@infofiend.com>, Ben Giddings
<bg-rubytalk@infofiend.com> 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
> 
> 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.  

It's not Ruby. It's a little domain specific language (DSL) tailored to
a particular problem.

> Even with your 
> explanation, I don't understand how it works, or how to modify it. 

It works by mapping by mapping the DSL to the normal Ruby syntax at
compile time. The exact details aren't really important unless you want
to create a similar macro.

> Is 
> "case" in your code the normal ruby "case" statement?  It doesn't look 
> like it.

No, it isn't the normal case statement. It's essentially a brand-new
statement type that may be implemented in terms of case or may not be.

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

Lisp and Dylan macros are far better than C macros. They're much more
powerful and safer as well. Much of the safety is because they're
hygienic macros which essentially means that names used in calls to the
macro refer to bindings that are in the scope of the macro call, and
names within the body of the macro refer to bindings in the scope of
the macro. In other words, unlike C macros, Lisp macros are not context
dependant and macros may define new variables without worrying about
conflicts with variables declared at the macro call site.

And in languages with a conventional syntax you can arrange things so
that macros come into play only in the appropiate context. For example,
a swap macro would only be used where a function call may be used. So,
unlike C, Dylan macros aren't applied blindly.

  -- Jesse
0
Reply jesjones (15) 8/4/2004 11:50:32 AM

In article <41106A47.7050901@weirichhouse.org>, Jim Weirich
<jim@weirichhouse.org> wrote:

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

Joel's solution was very cool. Certainly a lot better than any
non-macro solution I was able to come up with in a dynamic language I
was toying with.

> I'm curious 
> what a macro version would be like.  What would the implementation of 
> percent_case look like in Lisp or Dylan macros?

Here's a Dylan-ish macro:

macro percent-case is statement as body
    {percent-case ?cases end} =>    
        {local n = random()
        if false
            0
        ?cases 
        end}    
    
    cases:
        {} => {}
     
        {otherwise ?:body end} => 
            {else ?body}
       
        {?percent:float-literal case ?:body end ...} => 
            {elsif (n -= ?percent) <= 0.0 ?body ...}
end

In the first line we tell the compiler that we're augmenting the
language's statement production and that the macro expansion should be
reparsed using the body production.

The code in the first set of braces is the pattern for the main-rule
(this macro only has one main-rule). ?cases refers to an aux-rule named
cases. So, the macro matches code that starts with a 'percent-case'
token, followed by something that matches the aux-rule, followed by an
'end' token.

The code in braces after the => is what the macro expands to. In this
case some simple logic plus whatever the aux rule expands to.

The aux rule has three cases which are tried in order. 

1) It tries to match nothing, if this succeeds the aux rule expands to
nothing. 

2) It tries to match the otherwise clause, if this succeeds the aux
rule expands to an else clause.

3) It tries to match a floating-point literal followed by 'case', a
body production, and 'end'. If we get a match the aux rule expands to
an elsif clause. The ... means the aux rule is recursively invoked
again so we essentially iterate until we run out of cases.

Note that the macro defines an 'n" local variable. Because macros are
hygienic any references to n within the body of the macro refer to that
n even if the macro call site has a variable named n in scope.

  -- Jesse
0
Reply jesjones (15) 8/4/2004 11:50:33 AM

In article <9e7db91104080322005735a859@mail.gmail.com>, Austin Ziegler
<halostatue@gmail.com> wrote:

> On Wed, 4 Aug 2004 08:26:32 +0900, Jesse Jones <jesjones@mindspring.com>
> wrote:
> > percent-case
> >   0.75 case
> >     normal_hit
> >   end
> > 
> >   0.20 case
> >     critical_hit
> >   end
> > 
> >   otherwise
> >     kill_foe
> >   end
> > end
> 
> 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

A macro (or some clever code like Joel's) is better in two ways:

1) It's much easier to glance at numbers like 0.75 and 0.20 and figure
out the relative weighting. For me at least, it's a fair amount harder
to subtract two sets of numbers and compare them. Of course, you could
add comments listing the percentages, but it's always preferable to
rewrite the code so that it doesn't need such comments.

2) Adding new cases is harder if you have to deal with ranges. Not a
lot harder to be sure, but somewhat tedious.

Also note that this wasn't intended to be the last word in the utility
of macros. It was merely one of the simpler app specific examples I
could think of.

  -- Jesse
0
Reply jesjones (15) 8/4/2004 12:07:30 PM

On Wed, 4 Aug 2004 09:12:41 +0900, Joel VanderWerf  
<vjoel@PATH.Berkeley.EDU> 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.
>
> [...]

Or you could even do it like this, which looks (to me) even nicer than the  
macro solution :)

class PercentCase
   def initialize
     @v = rand(100)
   end
	
   def test(o)
     @v -= o
     @v <= 0
   end
end

class Integer
   alias_method :percentCase, :===
   def ===(o)
     if o.is_a? PercentCase
       o.test self
     else
       percentCase o
     end
   end
end

10.times do
   case PercentCase.new
   when 75
     puts 'normal_hit'
   when 20
     puts 'critical_hit'
   else
     puts 'kill_foe'
   end
end

-- 
exoticorn/farbrausch
0
Reply dennis.ranke (35) 8/4/2004 12:16:30 PM

Just some minor remark...

"Joel VanderWerf" <vjoel@PATH.Berkeley.EDU> schrieb im Newsbeitrag
news:41103008.7030107@path.berkeley.edu...
> 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

You could get rid of the "self" if you defined a method that receives a
hash along the lines of:

   default do

     values( :x => 0, :xDot => 20 )
     start SlowDown
   end

Or even remove the values setting from the block.

Since I don't know the inner workings I don't know whether there are other
factors that prevent this solution.  Just an idea.

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

So you create C code and compile it?

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

See above.

Regards

    robert

0
Reply bob.news (3805) 8/4/2004 12:21:57 PM

In article <41103008.7030107@path.berkeley.edu>, Joel VanderWerf
<vjoel@PATH.Berkeley.EDU> 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....

Between Ruby and Lisp? To tell the truth I'm not entirely sure. The
pickaxe book does a lousy job explaining the differences between blocks
and proc objects. I'm still not sure why Ruby needs two concepts when
Lisp and other languages get by with one. I also don't know why Ruby
uses that weird yield syntax.

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

Very nice Joel. Macro's wouldn't seem to be a huge improvement except
that you could inline code instead of turning everything into blocks.

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

I meant just what I said: that macros allow you to control the order of
evaluation of expressions. Although you're probably right that I should
have pointed out that blocks allow you to do the same.

  -- Jesse
0
Reply jesjones (15) 8/4/2004 12:36:42 PM

On Wednesday, August 4, 2004, 10:41:43 PM, Jesse 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.

Lisp lambda functions *do* do everything that Ruby blocks do.  But
doing is not the same as being.  I wrote that they are not equivalent.
The difference is syntax.  It's an artifical distinction that works
tremendously well in practice at expressing the solution in code.

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

> Between Ruby and Lisp? To tell the truth I'm not entirely sure. The
> pickaxe book does a lousy job explaining the differences between blocks
> and proc objects. I'm still not sure why Ruby needs two concepts when
> Lisp and other languages get by with one. I also don't know why Ruby
> uses that weird yield syntax.

On the one hand you advocate macros to make syntax more expressive; on
the other you advocate rolling everything back into anonymous
functions with no visual distinctions.  *shrug*

Gavin



0
Reply gsinclair1 (862) 8/4/2004 3:01:09 PM

On Wed, 4 Aug 2004 21:11:30 +0900, Jesse Jones <jesjones@mindspring.com> wrote:
> In article <9e7db91104080322005735a859@mail.gmail.com>, Austin Ziegler
> <halostatue@gmail.com> wrote:
> > On Wed, 4 Aug 2004 08:26:32 +0900, Jesse Jones <jesjones@mindspring.com>
> > wrote:
> > > percent-case
> > >   0.75 case
> > >     normal_hit
> > >   end
> > >
> > >   0.20 case
> > >     critical_hit
> > >   end
> > >
> > >   otherwise
> > >     kill_foe
> > >   end
> > > end
> > 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
> A macro (or some clever code like Joel's) is better in two ways:
> 
> 1) It's much easier to glance at numbers like 0.75 and 0.20 and figure
> out the relative weighting. For me at least, it's a fair amount harder
> to subtract two sets of numbers and compare them. Of course, you could
> add comments listing the percentages, but it's always preferable to
> rewrite the code so that it doesn't need such comments.

Hmm. I disagree. I mean, it's better to deal with code like Joel's (or
my Attacker class that I presented, which even makes sure you can't go
over 100%), because then you've treated the cases as closures and
objectified the whole thing, so that you don't have to do the numbers,
but basically (0 .. 75) says very clearly that you'll accept any value
in that range. It's harder to be dynamic, but you'll note that my
Attacker class (the second possibility in the email to which you
responded) makes that easy and still uses the range comparison.

I don't think that a domain-specific language is needed for that case.
The only situation I can think of that came up recently was the one
where someone wanted to introduce the a new operator ":="; that could
be handled with a domain-specific language with ease.

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


0
Reply halostatue (1713) 8/4/2004 3:12:11 PM

Jesse Jones wrote:
> In article <41102A98.806@infofiend.com>, Ben Giddings
> <bg-rubytalk@infofiend.com> wrote:
>>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.  
> 
> It's not Ruby. It's a little domain specific language (DSL) tailored to
> a particular problem.

I don't have an opinion on macros per se, but DSLs are (in my opinion) a 
poor application for macros. To paraphrase Dennis Ritchie, if you want 
racc, you know where to find it.

I'm a big fan of DSLs, but I like to build them up from the grammar.

Steve


0
Reply steven.jenkins (141) 8/4/2004 4:03:30 PM

In article <138135881306.20040805010035@soyabean.com.au>, Gavin
Sinclair <gsinclair@soyabean.com.au> wrote:

> On Wednesday, August 4, 2004, 10:41:43 PM, Jesse wrote:
> 
> > Between Ruby and Lisp? To tell the truth I'm not entirely sure. The
> > pickaxe book does a lousy job explaining the differences between blocks
> > and proc objects. I'm still not sure why Ruby needs two concepts when
> > Lisp and other languages get by with one. I also don't know why Ruby
> > uses that weird yield syntax.
> 
> On the one hand you advocate macros to make syntax more expressive; on
> the other you advocate rolling everything back into anonymous
> functions with no visual distinctions.  *shrug*

Well, what are the differences between blocks and proc objects? As far
as I can tell the only difference is that proc objects are explicitly
declared in method argument lists and are first class objects. Blocks,
on the other hand, are not listed in the method list (unless you use
&block to create a proc) and called using yield.

How does that make the language more expressive? Implicitly passing
arguments seems actively bad and using "yield lhs rhs" seems worse than
using "compare.call lhs rhs".

  -- Jesse
0
Reply jesjones (15) 8/4/2004 9:10:57 PM

In article <411108CE.5060805@ieee.org>, Steven Jenkins
<steven.jenkins@ieee.org> wrote:

> Jesse Jones wrote:
> > In article <41102A98.806@infofiend.com>, Ben Giddings
> > <bg-rubytalk@infofiend.com> wrote:
> >>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.  
> > 
> > It's not Ruby. It's a little domain specific language (DSL) tailored to
> > a particular problem.
> 
> I don't have an opinion on macros per se, but DSLs are (in my opinion) a 
> poor application for macros. To paraphrase Dennis Ritchie, if you want 
> racc, you know where to find it.
> 
> I'm a big fan of DSLs, but I like to build them up from the grammar.

Which is *exactly* what Dylan macros do. When you define a new Dylan
macro you're directly extending Dylan's grammar. But, unlike racc, your
domain specific language is integrated into the base language and you
can use the compiler to handle lexing and parsing.

Here's a Scheme paper that contrasts the traditional approach to DSLs
with a macro based one: <http://www.ai.mit.edu/people/shivers/ll.ps>.

  -- Jesse
0
Reply jesjones (15) 8/4/2004 9:10:59 PM

Jesse Jones wrote:
>>>percent-case
>>>  0.75 case
>>>    normal_hit
>>>  end
>>>
>>>  0.20 case
>>>    critical_hit
>>>  end
>>>
>>>  otherwise
>>>    kill_foe
>>>  end
>>>end
>>
>>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
> 
> 
> A macro (or some clever code like Joel's) is better in two ways:
> 
> 1) It's much easier to glance at numbers like 0.75 and 0.20 and figure
> out the relative weighting. For me at least, it's a fair amount harder
> to subtract two sets of numbers and compare them. Of course, you could
> add comments listing the percentages, but it's always preferable to
> rewrite the code so that it doesn't need such comments.

I don't think you're right on this point. In the case that was
presented:

 >   0.75 case
 >     normal_hit
 >   end

Does this mean 0.75 and above, or 0.75 and below? You won't know
this unless you look up the macro's definition. Here, however:

 >    when (0.0 .. 0.75)
 >       normal_hit

I don't think you can be confused on that point.

The macro version has made the code much less clear in my mind, which is
precisely the argument against macros in ruby.

-- 
Rando Christensen
<eyez@illuzionz.org>


0
Reply eyez (32) 8/4/2004 9:16:52 PM

Rando Christensen <eyez@illuzionz.org> writes:

> The macro version has made the code much less clear in my mind, which is
> precisely the argument against macros in ruby.

Actually, the example Jesse gave was probably bad. A macro, like any
other language construct, can be created to enhance clarity or
obfucate. Thus, one should not make a hasty judgement based on that
one example.

How about this example (which I hope is a better example of
easy-with-macro,-hard-(impossible?)-without):

Suppose we want to do self-documenting methods (not source file),
similar to docstring in emacs:

An implementation with macro would look something like:

def foo(arg)
    "This method will return <code>arg</code>"
    arg 
end 

Sure, you can implement this without macro (trivial implementation):

DOCSTRINGS={}
def def_with_docstring(klazz, method_name, docstring, arg_spec, body)
  id = "#{klazz.to_s}##{method_name}"
  DOCSTRINGS[id] = docstring
  klazz.module_eval(%{def #{method_name}(#{arg_spec})
                           #{body}
                      end}, __FILE__, 9999) # wish that there isn't
                                            # anything on line 9999.
end

def_with_docstring(Object, "foo", "returns arg", "arg", "arg")
  
Object.new.foo("hi") # => "hi"

Yes, I am aware you can do more sophisticated implementation so you
can make the above nicer. Yet, as nice as it will be, it won't be as
simple as the example with macro; there will be quirkiness caused by
the language syntax.

For example, rake has this in its documentation: "NOTE: Because of a
quirk in Ruby syntax, parenthesis are required on rule when the first
argument is a regular expression."

Another example: I am not even sure you can turn the above example to
take lambdas as the body. Being able to take lambdas would be nice as
you can take advantage of your editor's syntax highlighting and
auto-indent.


YS.


0
Reply ysantoso-rubytalk1 (130) 8/4/2004 10:51:44 PM

On Thursday, August 5, 2004, 7:11:29 AM, Jesse wrote:

> In article <138135881306.20040805010035@soyabean.com.au>, Gavin
> Sinclair <gsinclair@soyabean.com.au> wrote:

>> On Wednesday, August 4, 2004, 10:41:43 PM, Jesse wrote:
>> 
>> > Between Ruby and Lisp? To tell the truth I'm not entirely sure. The
>> > pickaxe book does a lousy job explaining the differences between blocks
>> > and proc objects. I'm still not sure why Ruby needs two concepts when
>> > Lisp and other languages get by with one. I also don't know why Ruby
>> > uses that weird yield syntax.
>> 
>> On the one hand you advocate macros to make syntax more expressive; on
>> the other you advocate rolling everything back into anonymous
>> functions with no visual distinctions.  *shrug*

> Well, what are the differences between blocks and proc objects? As far
> as I can tell the only difference is that proc objects are explicitly
> declared in method argument lists and are first class objects. Blocks,
> on the other hand, are not listed in the method list (unless you use
> &block to create a proc) and called using yield.

> How does that make the language more expressive?

Which do you prefer?

   (1..10).map { |n| n ** 2 }

   (1..10).map( lambda { |n| n ** 2 } )

(If the latter, then I guess nothing further can be said.)

Blocks are something that are used _all the time_ in Ruby.  Perhaps
more than you appreciate.  They are semantically the same as explicit
blocks, but the syntax is optimised for the common use.  And it's not
limiting:

  square = lambda { |n| n ** 2 }
  (1..10).map(&square)

Please remember Joel's "percent-case" solution, extensively using
blocks.  That was nothing if not expressive.  Ruby can be bent into
1000 different shapes, primarily thanks to blocks.  Those shapes would
be possible without them (just use explicit lambdas) but not nearly as
appealing, and therefore people wouldn't bother, and the world would
be poorer.

> Implicitly passing arguments seems actively bad and using "yield lhs
> rhs" seems worse than using "compare.call lhs rhs".

Same comments as above apply.  "Yield" is the perfect English word to
describe the behaviour.

For some words from the source, I recommend you look up Matz's
interviews with Bill Venners on artima.com.

Ruby is a lot of things, but it's not a LISP/Dylan wanna-be.  You do
yourself no favours by insisting on looking at it from that angle.

Cheers,
Gavin



0
Reply gsinclair1 (862) 8/4/2004 11:06:22 PM

In article <161164987028.20040805090540@soyabean.com.au>, Gavin
Sinclair <gsinclair@soyabean.com.au> wrote:

> Which do you prefer?
> 
>    (1..10).map { |n| n ** 2 }
> 
>    (1..10).map( lambda { |n| n ** 2 } )

I prefer a light-weight syntax for something as commonly used as
anonymous functions. On the other hand, I still don't know why {|n| n
** 2} can't simply create a proc object. 

> Please remember Joel's "percent-case" solution, extensively using
> blocks.  That was nothing if not expressive.  

Yes, a beautiful example of the flexibility of Ruby. But I don't see
how having both blocks and proc objects improves Ruby's expressiveness.


> Ruby can be bent into
> 1000 different shapes, primarily thanks to blocks.  

Plenty of languages have blocks, but fewer languages could build
declarative structures as nicely as Joel was able to do. You also need
instance_eval and it helps a lot if the syntax is fairly free-form so
you can omit parenthesis in function calls.

> > Implicitly passing arguments seems actively bad and using "yield lhs
> > rhs" seems worse than using "compare.call lhs rhs".
> 
> Same comments as above apply.  "Yield" is the perfect English word to
> describe the behaviour.

Sure, it's a good word to describe what the interpreter will do when it
hits that line, but it completely fails to describe the more important
semantic behavior. And if the language already has proc objects why
bother supporting yield?

  -- Jesse
0
Reply jesjones (15) 8/5/2004 3:30:33 AM

In article <41115242.1020602@illuzionz.org>, Rando Christensen
<eyez@illuzionz.org> wrote:

> I don't think you're right on this point. In the case that was
> presented:
> 
>  >   0.75 case
>  >     normal_hit
>  >   end
> 
> Does this mean 0.75 and above, or 0.75 and below? You won't know
> this unless you look up the macro's definition. 

The macro is called percent-case for a reason. "0.75 case" means that
case will be taken 75% of the time. It should be possible to change the
macro so that it would support things like "75% case" which might be
better if you want a macro that may be used in apps where its usage
would be uncommon.

> The macro version has made the code much less clear in my mind, which is
> precisely the argument against macros in ruby.

I don't see that. In any non-trivial program there's plenty of code
that isn't perfectly intentional. I'm sure you don't consider method
calls a bad thing because it's often not clear exactly what they do.
Macros are no different: they can make code clearer than hand-coding
the solution, but if you're not familiar with the app you'll probably
have to familiarize yourself with the commonly used macros just as you
would do with the commonly used classes.

  -- Jesse
0
Reply jesjones (15) 8/5/2004 3:36:33 AM

Jesse Jones wrote:
> Yes, a beautiful example of the flexibility of Ruby. But I don't see
> how having both blocks and proc objects improves Ruby's expressiveness.

It doesn't.  You could drop the implicitly passed blocks and always deal 
with an explicit &block argument and not miss a bit of expressiveness 
There is a performance difference difference however, and I suspect that 
is the real reason why the yield syntax is supported (i.e. to avoid the 
overhead of reifying blocks into full fledged objects).  Perhaps a 
future optimization will make the &block syntax as efficient as the 
implicit yield syntax.  When that happens, the "block" concept can 
retreat to the interior of the interpretor (where it belongs) and not 
haunt developers with this weird "almost a proc, but not quite" 
semantics.  At least that's my view.

Actually what surprises me most about Ruby's one-block per method 
technique is how flexible it turns out to be.  One would think that 
having the ability to pass multiple blocks would be big improvement, but 
I suspect that the improvement in flexibility is merely incremental.

What surprises me about Ruby is the about of expressiveness you get, 
even though you are limited to only one block per method call.  You 
would think that having the ability to pass multiple closures to a 
method would be a big improvement,

 > Plenty of languages have blocks, but fewer languages could build
 > declarative structures as nicely as Joel was able to do. You also need
 > instance_eval and it helps a lot if the syntax is fairly free-form so
 > you can omit parenthesis in function calls.

All those help.  I've used both Lisp and Forth and really admire their 
ability to create tailored languages for a domain.  I think Ruby comes 
close to their level of flexibility, but does it with different tools 
that either Forth or  Lisp (i.e. macros and run-time code generation).

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


0
Reply jim9733 (726) 8/5/2004 4:24:16 AM

"Yohanes Santoso" <ysantoso-rubytalk@dessyku.is-a-geek.org> schrieb im
Newsbeitrag news:877jseplp6.fsf@dessyku.is-a-geek.org...
> Rando Christensen <eyez@illuzionz.org> writes:
>
> > The macro version has made the code much less clear in my mind, which
is
> > precisely the argument against macros in ruby.
>
> Actually, the example Jesse gave was probably bad. A macro, like any
> other language construct, can be created to enhance clarity or
> obfucate. Thus, one should not make a hasty judgement based on that
> one example.
>
> How about this example (which I hope is a better example of
> easy-with-macro,-hard-(impossible?)-without):
>
> Suppose we want to do self-documenting methods (not source file),
> similar to docstring in emacs:
>
> An implementation with macro would look something like:
>
> def foo(arg)
>     "This method will return <code>arg</code>"
>     arg
> end
>
> Sure, you can implement this without macro (trivial implementation):
>
> DOCSTRINGS={}
> def def_with_docstring(klazz, method_name, docstring, arg_spec, body)
>   id = "#{klazz.to_s}##{method_name}"
>   DOCSTRINGS[id] = docstring
>   klazz.module_eval(%{def #{method_name}(#{arg_spec})
>                            #{body}
>                       end}, __FILE__, 9999) # wish that there isn't
>                                             # anything on line 9999.
> end

This will print wrong file names for all methods not defined in that file.
I'd prefer something like this:

module DocMix
  def doc(item, text)
    (@docs ||= {})[item.to_s] = "#{item} defined in #{caller[0]}: #{text}"
  end

  def get_doc(item)
     @docs ? @docs[item.to_s] : nil
  end
end

class Class ; include DocMix end
class Module ; include DocMix end

class Foo
  doc :bar, "Method that prints hallo 'name'"
  def bar(name)
    puts "hallo #{name}"
  end
end

puts Foo.get_doc( :bar )

module Test
  doc :"Test.bar", "Method that prints hallo 'name'"
  def self.bar(name)
    puts "hallo #{name}"
  end
end

puts Test.get_doc( :"Test.bar" )

Of course one could easily add argument names etc.  Personally I never
needed Macros until now and I've yet to see a use case where I would
really need them.

> def_with_docstring(Object, "foo", "returns arg", "arg", "arg")
>
> Object.new.foo("hi") # => "hi"
>
> Yes, I am aware you can do more sophisticated implementation so you
> can make the above nicer. Yet, as nice as it will be, it won't be as
> simple as the example with macro; there will be quirkiness caused by
> the language syntax.
>
> For example, rake has this in its documentation: "NOTE: Because of a
> quirk in Ruby syntax, parenthesis are required on rule when the first
> argument is a regular expression."
>
> Another example: I am not even sure you can turn the above example to
> take lambdas as the body. Being able to take lambdas would be nice as
> you can take advantage of your editor's syntax highlighting and
> auto-indent.

You could do that.  But you couldn't define methods that receive a body as
lamba that way.  That's a restriction of define_method which you would
have to use in this case.

Regards

    robert

0
Reply bob.news (3805) 8/5/2004 9:06:57 AM

Hi,

On Thu, 05 Aug 2004 03:30:33 +0000, Jesse Jones wrote:
>> Which do you prefer?
>> 
>>    (1..10).map { |n| n ** 2 }
>> 
>>    (1..10).map( lambda { |n| n ** 2 } )

shouldn't that be '(1..10).map( &lambda { |n| n ** 2 } )'?

> 
> I prefer a light-weight syntax for something as commonly used as anonymous
> functions. On the other hand, I still don't know why {|n| n ** 2} can't
> simply create a proc object.

It can! Wether or not a proc object gets created depends on the
called method.  If that method uses yield, then no object is
created.  If however it captures a block using the '&' operator,
then a Proc object gets created.  

On the other hand, a Proc created this way is different from one 
created using Proc.new or lambda.

The difference which I can think of are:
- Argument passing, a block isn't strict about the arguments
  it gets passed, (i.e. it can take any argument).  A Proc 
  object in contrast is strict (like methods).  The reason I
  can think of is that it is often usefull not to get all arguments
  in a block, i.e. 3.times{ puts "Hi, world!" }

- The place where return goes to: in a block 'return' returns
  from the method in which it is defined.  In a lambda (Proc)
  it returns from the lambda expression.

> 
>> Please remember Joel's "percent-case" solution, extensively using
>> blocks.  That was nothing if not expressive.
> 
> Yes, a beautiful example of the flexibility of Ruby. But I don't see how
> having both blocks and proc objects improves Ruby's expressiveness.
> 
> 

Well, it is very expressive to be able to 'return' from a block.
That 's one of those thing I find uncomfortable in lisp and
scheme.  Sure, there is a good reason there is no 'return' in 
scheme: every statement is an expression, it wouldn't know where
to return from.  
I find it much more practical in Ruby, (with some gotha's, like
LocalJumpError).

Also block make it possible to do 3.times { puts "hello"} as 
explained above.  That's more expressive than having to type
3.times { |i| puts "hello"}

> Sure, it's a good word to describe what the interpreter will do when it
> hits that line, but it completely fails to describe the more important
> semantic behavior. And if the language already has proc objects why
> bother supporting yield?
> 
>   -- Jesse

I believe yield is there historically.  In early versions of Ruby
yield used to be the only way to pass arguments to blocks.  Then
in was unified with Proc objects.  I may be totally mistaken about
this.

The other thing is performance.  Creating a proc object is quite
expensive.

Also, but perhaps less important, it is a little easier to 
type yield, than to create block, and use block.call every
time.

I hope this explains a little why I find Ruby's way very 
expressive.  I believe Ruby isn't semantically as clean as
Scheme or SmallTalk.  (I don't think lisp is semantically very
clean).  However for me it has the right mix of being consistent 
and being practical.

Regards,
KB
0
Reply kristof1 (235) 8/5/2004 9:55:29 AM

On Thu, 05 Aug 2004 13:24:16 +0900, Jim Weirich wrote:
> What surprises me about Ruby is the about of expressiveness you get, even
> though you are limited to only one block per method call.  You would think
> that having the ability to pass multiple closures to a method would be a
> big improvement,

That's possible, but not in a block passing way (if that is 
what you mean).  You can always create a closure using 
lambda and pass it to the method like any other value.

Regards,
KB


0
Reply kristof1 (235) 8/5/2004 10:01:04 AM

> def_with_docstring(Object, "foo", "returns arg", "arg", "arg")
>   
> Object.new.foo("hi") # => "hi"
> 
> Yes, I am aware you can do more sophisticated implementation so you
> can make the above nicer. Yet, as nice as it will be, it won't be as
> simple as the example with macro; there will be quirkiness caused by
> the language syntax.

not that much, did you see the MetaTags system or the various
implementations of docstrings?

Something on the lines of:

doc 'this things does foo'
def foo
 ...
end

is quite trivial to do.

> For example, rake has this in its documentation: "NOTE: Because of a
> quirk in Ruby syntax, parenthesis are required on rule when the first
> argument is a regular expression."

IIRC this is related to bison not being good enough :)
0
Reply surrender_it1 (15) 8/5/2004 10:06:49 AM

"Kristof Bastiaensen" <kristof@vleeuwen.org> schrieb im Newsbeitrag
news:pan.2004.08.05.09.55.29.582255@vleeuwen.org...
> Hi,
>
> On Thu, 05 Aug 2004 03:30:33 +0000, Jesse Jones wrote:
> >> Which do you prefer?
> >>
> >>    (1..10).map { |n| n ** 2 }
> >>
> >>    (1..10).map( lambda { |n| n ** 2 } )
>
> shouldn't that be '(1..10).map( &lambda { |n| n ** 2 } )'?
>
> >
> > I prefer a light-weight syntax for something as commonly used as
anonymous
> > functions. On the other hand, I still don't know why {|n| n ** 2}
can't
> > simply create a proc object.
>
> It can! Wether or not a proc object gets created depends on the
> called method.  If that method uses yield, then no object is
> created.  If however it captures a block using the '&' operator,
> then a Proc object gets created.
>
> On the other hand, a Proc created this way is different from one
> created using Proc.new or lambda.
>
> The difference which I can think of are:
> - Argument passing, a block isn't strict about the arguments
>   it gets passed, (i.e. it can take any argument).  A Proc
>   object in contrast is strict (like methods).  The reason I
>   can think of is that it is often usefull not to get all arguments
>   in a block, i.e. 3.times{ puts "Hi, world!" }

I don't know what exactly you mean by this.  Argument passing is the same
for blocks and procs IMHO.  Can you explain the difference?

> - The place where return goes to: in a block 'return' returns
>   from the method in which it is defined.  In a lambda (Proc)
>   it returns from the lambda expression.

Ah, thanks for reminding me!  That's IMHO the single most crucial
difference between blocks and procs created from blocks vs. stand alone
lambdas / procs.  And it's a really important difference!

Kind regards

    robert



>
> >
> >> Please remember Joel's "percent-case" solution, extensively using
> >> blocks.  That was nothing if not expressive.
> >
> > Yes, a beautiful example of the flexibility of Ruby. But I don't see
how
> > having both blocks and proc objects improves Ruby's expressiveness.
> >
> >
>
> Well, it is very expressive to be able to 'return' from a block.
> That 's one of those thing I find uncomfortable in lisp and
> scheme.  Sure, there is a good reason there is no 'return' in
> scheme: every statement is an expression, it wouldn't know where
> to return from.
> I find it much more practical in Ruby, (with some gotha's, like
> LocalJumpError).
>
> Also block make it possible to do 3.times { puts "hello"} as
> explained above.  That's more expressive than having to type
> 3.times { |i| puts "hello"}
>
> > Sure, it's a good word to describe what the interpreter will do when
it
> > hits that line, but it completely fails to describe the more important
> > semantic behavior. And if the language already has proc objects why
> > bother supporting yield?
> >
> >   -- Jesse
>
> I believe yield is there historically.  In early versions of Ruby
> yield used to be the only way to pass arguments to blocks.  Then
> in was unified with Proc objects.  I may be totally mistaken about
> this.
>
> The other thing is performance.  Creating a proc object is quite
> expensive.
>
> Also, but perhaps less important, it is a little easier to
> type yield, than to create block, and use block.call every
> time.
>
> I hope this explains a little why I find Ruby's way very
> expressive.  I believe Ruby isn't semantically as clean as
> Scheme or SmallTalk.  (I don't think lisp is semantically very
> clean).  However for me it has the right mix of being consistent
> and being practical.
>
> Regards,
> KB

0
Reply bob.news (3805) 8/5/2004 10:15:20 AM

surrender_it@yahoo.it (gabriele renzi @ google) writes:

>> def_with_docstring(Object, "foo", "returns arg", "arg", "arg")
>>   
>> Object.new.foo("hi") # => "hi"
>> 
>> Yes, I am aware you can do more sophisticated implementation so you
>> can make the above nicer. Yet, as nice as it will be, it won't be as
>> simple as the example with macro; there will be quirkiness caused by
>> the language syntax.
>
> not that much, did you see the MetaTags system or the various
> implementations of docstrings?
>
> Something on the lines of:
>
> doc 'this things does foo'
> def foo
>  ...
> end

More like:
      class_info <<-DOC
        !Class: MyClass
        !Desc:  This class does something
      
        !attr a: A: The "A" attribute
        !attr b: B
      DOC
      class MyClass
        ...
      end

and this is what the doc has to say about the '!Class' tag:

   !Class - A string indicating the fully-qualified class name.  This
             is a required field.


Robert's example is about similar to what MetaTags does. Thus, they
both make one to repeat one self; you have to say: "this docstring is
for this method of this class", even if the information is available
nearby. I think this counts as quirkiness.

My point was not whether it is possible to do something in Ruby. The
point was to show Rando Christensen that macro can be used to enhance
clarity as well as to obfuscate, and that some things are easier done
with macro then without. I think my example shows that with macro, you
can extend the language naturally (instead of having to repeat
yourself if done within the language's limitation).

>> For example, rake has this in its documentation: "NOTE: Because of a
>> quirk in Ruby syntax, parenthesis are required on rule when the first
>> argument is a regular expression."
>
> IIRC this is related to bison not being good enough :)

Somewhere, somehow, there will always be deficiencies. With macro, you
can alleviate those somewhat. Not that I think macro would help rake
in the above case since that is a syntax problem, rather than semantic
problem (I have no idea if macro can save you from invalid syntax
since the only macro system I've used is lisp's where the syntax is
not as rich as ruby's and also doesn't work if you have an invalid
syntax anyway).

I am not advocating for macro in Ruby. I am neutral; I do not care if
there is macro or not. If there is macro, then that's great, although
there are only so many legitimate usages. If there isn't, it is also
not such a big of a loss since there are only few legitimiate usages.

Even the above example (repeating yourself), can be somewhat
eliminated with the help of a good editor.

Have a nice day, thanks for the input! I didn't know about MetaTags
before. Thank you.


YS.


0
Reply ysantoso-rubytalk1 (130) 8/5/2004 10:45:15 AM

On Thu, 05 Aug 2004 12:15:20 +0200, Robert Klemme wrote:
>> The difference which I can think of are: - Argument passing, a block
>> isn't strict about the arguments
>>   it gets passed, (i.e. it can take any argument).  A Proc object in
>>   contrast is strict (like methods).  The reason I can think of is that
>>   it is often usefull not to get all arguments in a block, i.e. 3.times{
>>   puts "Hi, world!" }
> 
> I don't know what exactly you mean by this.  Argument passing is the same
> for blocks and procs IMHO.  Can you explain the difference?

Oh, it seems I was wrong (after looking it up with ri).
What I said in the previous post only applies to procs created
with lambda (including the return thing), not procs created with
Proc.new.
So I guess that Proc.new creates exactly the same Proc object
as a block does (in the case that a Proc object gets created
from a block).  Only lambda expressions are different.
That would also mean that the best way to get rid of the 
LocalJumpError problem is to use a lambda expression.

Here is an example about what I meant with argument passing:

b1 = Proc.new { |a,b| 
   puts "a == #{a.inspect}, b == #{b.inspect}" }
b2 = lambda { |a,b|
   puts "a == #{a.inspect}, b == #{b.inspect}" }

b1[1]
b2[1]
#--- output: ----
a == 1, b == nil
/tmp/blocks.rb:5: wrong number of arguments (1 for 2) (ArgumentError)
        from /tmp/blocks.rb:5:in `[]'
        from /tmp/blocks.rb:9
#----------------

Sorry for the confusion.

Cheers,
KB
0
Reply kristof1 (235) 8/5/2004 11:57:09 AM

> What I said in the previous post only applies to procs created
> with lambda (including the return thing), not procs created with
> Proc.new.

and is there any difference between lambda {...} and proc {...} ?



0
Reply k4003 (27) 8/5/2004 12:03:37 PM

On Thu, 05 Aug 2004 21:03:37 +0900, Caio Chassot wrote:

>> What I said in the previous post only applies to procs created with
>> lambda (including the return thing), not procs created with Proc.new.
> 
> and is there any difference between lambda {...} and proc {...} ?

Well, according to ri they are the same method, and a short
investigation seemes to confirm that.

KB
0
Reply kristof1 (235) 8/5/2004 12:13:18 PM

Yohanes Santoso wrote...

>and this is what the doc has to say about the '!Class' tag:
>
>   !Class - A string indicating the fully-qualified class name.  This
>             is a required field.
>
>
>Robert's example is about similar to what MetaTags does. Thus, they
>both make one to repeat one self; you have to say: "this docstring is
>for this method of this class", even if the information is available
>nearby. I think this counts as quirkiness.

Actually, you don't:

# utterly broken but shows the concept:

[nickel@UltimaThule nickel]$ irb --simple-prompt
>> def doc str
>>  @is_test=str
>> end
=> nil
>> class Module
>>  Docs=Hash.new
>>  def method_added(id)
>>   Docs[id]=@is_test if @is_test
>>  end
>> end
=> nil
>> class Foo
>>  doc "this speaks"
>>  def meow
>>   "meoww"
>>  end
>> end
=> nil
>> Module::Docs[:meow]
=> "this speaks"
>> module M
>>  def foo
>>  end
>>  doc "yuppy"
>>  def baz
>>   'naia'
>>  end
>> end
=> nil
>> Module::Docs[:baz]
=> "yuppy"

anyway, I agree this is really far from perfect :)
0
Reply surrender_it1 (15) 8/5/2004 12:19:09 PM

On Thu, 5 Aug 2004 13:24:16 +0900, Jim Weirich <jim@weirichhouse.org> wrote:
> Actually what surprises me most about Ruby's one-block per method
> technique is how flexible it turns out to be.  One would think that
> having the ability to pass multiple blocks would be big improvement, but
> I suspect that the improvement in flexibility is merely incremental.
> 
> What surprises me about Ruby is the about of expressiveness you get,
> even though you are limited to only one block per method call.  You
> would think that having the ability to pass multiple closures to a
> method would be a big improvement,

When I ported Diff::LCS, the Perl Algorithm::Diff version from which I
started used a hash of anonymous procs. I modified this to use a
callback *object*. I think that by the time you start needing multiple
procs, it's just as easy (or easier) to require an object that
encapsulates that.

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


0
Reply halostatue (1713) 8/5/2004 1:27:10 PM

On Thursday, August 5, 2004, 7:56:34 PM, Kristof wrote:

> Hi,

> On Thu, 05 Aug 2004 03:30:33 +0000, Jesse Jones wrote:
>>> Which do you prefer?
>>> 
>>>    (1..10).map { |n| n ** 2 }
>>> 
>>>    (1..10).map( lambda { |n| n ** 2 } )

> shouldn't that be '(1..10).map( &lambda { |n| n ** 2 } )'?

No.  Jesse expressed a distaste for implicitly passed arguments (i.e.
a block being passed to the method as a hidden, or a special,
parameter.  So I presented the above examples as two *different*
possible #map interfaces.  In the first one, the block "parameter" is
implicit; in the second one, explicit.

>> 
>> I prefer a light-weight syntax for something as commonly used as anonymous
>> functions. On the other hand, I still don't know why {|n| n ** 2} can't
>> simply create a proc object.

> It can! Wether or not a proc object gets created depends on the
> called method.  [...]

That's context-dependent.  My impression is that Jesse thinks that
code snippet in *any* context should evaluate to a lambda.

> On the other hand, a Proc created this way is different from one 
> created using Proc.new or lambda.

> The difference which I can think of are:
> - Argument passing, a block isn't strict about the arguments
>   it gets passed, (i.e. it can take any argument).  A Proc 
>   object in contrast is strict (like methods).  The reason I
>   can think of is that it is often usefull not to get all arguments
>   in a block, i.e. 3.times{ puts "Hi, world!" }

> - The place where return goes to: in a block 'return' returns
>   from the method in which it is defined.  In a lambda (Proc)
>   it returns from the lambda expression.

Thanks for pointing these out; they didn't come to mind before.

<snip/>

Gavin



0
Reply gsinclair1 (862) 8/5/2004 3:24:41 PM

On Monday 02 August 2004 02:16, George Moschovitis wrote:
> 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.

I have to say that, for me, Ruby procs work just fine in place of macros, 
except if I could change one thing, it would be this:

The ability to modify the binding in which procs run.

Sometimes I really wish I could make a call in a method or class definition, 
and have that proc run in the context where I call it, instead of where it's 
defined.  Like so:

module ModOne
  @var = "one"

  TestProc = Proc.new do
    @var
  end
end

module ModTwo
  @var = "two"

  ModOne::TestProc.call  #=> "two"
end

...perhaps making a new class named "Macro" which is essentially Proc, but 
which executes in the current binding instead of the binding where it was 
created.

	Sean O'Dell


0
Reply sean675 (430) 8/5/2004 4:38:08 PM

On Fri, Aug 06, 2004 at 01:38:08AM +0900, Sean O'Dell wrote:
> module ModOne
>   @var = "one"
> 
>   TestProc = Proc.new do
>     @var
>   end
> end
> 
> module ModTwo
>   @var = "two"
> 
>   ModOne::TestProc.call  #=> "two"
> end

In this particular case, it can be solved with 

batsman@tux-chan:/tmp$ cat hfdh.rb
module ModOne
    @var = "one"

    TestProc = Proc.new do
        @var
    end
end

module ModTwo
    @var = "two"

    p instance_eval(&ModOne::TestProc)  #=> "two"
end

batsman@tux-chan:/tmp$ ruby hfdh.rb
"two"

> ...perhaps making a new class named "Macro" which is essentially Proc, but 
> which executes in the current binding instead of the binding where it was 
> created.

I do agree on the fact that better control of the bindings (as often
proposed in the past) would make several of the things people would want
to do with macros possible without further language modifications.



-- 
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com



0
Reply batsman.geo (482) 8/5/2004 4:56:47 PM

In article <60223680255.20040806012354@soyabean.com.au>, Gavin Sinclair
<gsinclair@soyabean.com.au> wrote:

> That's context-dependent.  My impression is that Jesse thinks that
> code snippet in *any* context should evaluate to a lambda.

No, my argument was that having lambda + Proc + method + block seemed
awfully confusing and I didn't see the justifications for doing so. 

For example, some have said that blocks are there for performance
reasons which is a reasonable argument, but also seems like a bit of a
copout. It seems like it would be nicer to do these sorts of
optimzations automatically in the interpreter instead of cluttering up
the language with ways to indicate that the optimization is applicable.

Similarly return returning from a method within a block, but returning
from the closure in a lambda expression was touted as a benefit. But,
to me, it'd be cleaner to use one closure construct and reuse break
which already has the local escape semantics.

  -- Jesse
0
Reply jesjones (15) 8/5/2004 8:16:15 PM

67 Replies
41 Views

(page loaded in 0.47 seconds)

Similiar Articles:


















7/23/2012 10:17:03 AM


Reply: