[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

About Extending Systax

Sam Kong

2/7/2006 5:48:00 PM

Hi!

A friend of mine challenged me with Smalltalk.
He's a big fan of Smalltalk.
He asked me what I can do if I want to add "try ~ finally ~" systax in
Ruby.
Yes, we already have "begin ~ ensure ~".
But he asked me whether Ruby is flexible enough to extend such a thing
without changing the language itself.
He said that Smalltalk doesn't have "try ~ finally ~" in the language
but can be defined without changing the language.

Personally, I don't think such flexibility is really needed.
However, I want to defend Ruby.
How would you react such an attack?

Disclaimer: This post is not for language flame war but for better
understanding of Ruby.

Thanks.

Sam

7 Answers

Jim Weirich

2/7/2006 6:24:00 PM

0

Sam Kong wrote:
> Hi!
>
> A friend of mine challenged me with Smalltalk.
> He's a big fan of Smalltalk.
> He asked me what I can do if I want to add "try ~ finally ~" systax in
> Ruby.
> Yes, we already have "begin ~ ensure ~".
> But he asked me whether Ruby is flexible enough to extend such a thing
> without changing the language itself.
> He said that Smalltalk doesn't have "try ~ finally ~" in the language
> but can be defined without changing the language.
>
> Personally, I don't think such flexibility is really needed.
> However, I want to defend Ruby.
> How would you react such an attack?

There are several ways this could be done ... here is one:

class TryFinally
def initialize(block)
@block = block
end
def finally
@block.call
ensure
yield
end
end

def try(&block)
TryFinally.new(block)
end

Usage:

try {
puts "Trying"
fail "oops"
}.finally {
puts "Always printed"
}

It is a bit little easier in Smalltalk because of the use of keywords in
an argument list, but still quite doable in Ruby.

--
-- Jim Weirich

--
Posted via http://www.ruby-....


MenTaLguY

2/7/2006 6:55:00 PM

0

Quoting David Vallner <david@vallner.net>:

> As to the actual implementation, I can at best think of a
> solution that wraps around begin / rescue / ensure - you still
> have to have some support for nonlocal exits from the runtime,

You could use continutations. Although they're more general, you
can still use them in much the same fashion as you would
setjmp/longjmp in C.

-mental


vanekl

2/7/2006 7:54:00 PM

0

Edward Faulkner

2/7/2006 8:36:00 PM

0

On Wed, Feb 08, 2006 at 03:54:39AM +0900, mental@rydia.net wrote:
> You could use continutations.

Indeed. With continuations you can build your own try/finally that
doesn't use the built in ruby begin/rescue at all:

$cc_stack = []

# takes two procs
def my_try(body, finally)
if callcc {|cc| $cc_stack << cc}
body.call
$cc_stack.pop
end
finally.call
end

def my_throw
$cc_stack.pop.call
end

You could play some tricks to get a nicer syntax, but that's the
general idea.

-Ed

Sam Kong

2/7/2006 9:03:00 PM

0


Jim Weirich wrote:
> Sam Kong wrote:
> > Hi!
> >
> > A friend of mine challenged me with Smalltalk.
> > He's a big fan of Smalltalk.
> > He asked me what I can do if I want to add "try ~ finally ~" systax in
> > Ruby.
> > Yes, we already have "begin ~ ensure ~".
> > But he asked me whether Ruby is flexible enough to extend such a thing
> > without changing the language itself.
> > He said that Smalltalk doesn't have "try ~ finally ~" in the language
> > but can be defined without changing the language.
> >
> > Personally, I don't think such flexibility is really needed.
> > However, I want to defend Ruby.
> > How would you react such an attack?
>
> There are several ways this could be done ... here is one:
>
> class TryFinally
> def initialize(block)
> @block = block
> end
> def finally
> @block.call
> ensure
> yield
> end
> end
>
> def try(&block)
> TryFinally.new(block)
> end
>
> Usage:
>
> try {
> puts "Trying"
> fail "oops"
> }.finally {
> puts "Always printed"
> }
>
> It is a bit little easier in Smalltalk because of the use of keywords in
> an argument list, but still quite doable in Ruby.

This is nice.
But my intention is not to make a new syntax that does the same thing.
Let's assume that there's no *ensure* syntax in Ruby.

Sam

Logan Capaldo

2/7/2006 10:19:00 PM

0

Here's another go at it using continuations:

% cat try_catch_throw.rb
class Flow
def initialize(parent = nil)
@exceptions = []
@parent = parent
@uncaught_exception = nil
end
def try(&block)
@try_block = block
end

def throw(exception)
caught = @exceptions.each do |key, value|
if key === exception
value.call(exception)
break true
end
end

unless caught == true
if @parent
@parent.throw(exception)
else
@uncaught_exception = exception
end
end
@cc.call
end

def finally(&block)
@finally = block
self
end

def catch(exception, &block)
@exceptions << [exception, block]
self
end
def go
callcc { |@cc| @try_block.call }
if @finally
@finally.call
end
if @uncaught_exception
STDERR.puts "Uncaught exception: #{@uncaught_exception}"
exit(1)
end
end

end




% cat test_flow.rb
require 'try_catch_throw'


handling2 = Flow.new
handling2.try {
handling3 = Flow.new(handling2)
handling3.try {
puts "Nested try"
handling3.throw "ERROR! in handling3"
}
handling3.go
}

handling2.catch(String) do |exception|
puts "handling2 Caught exception: #{exception}"
end

handling2.go

handling = Flow.new

handling.try {
puts "Hello"
handling.throw "ERROR!"
puts "World"
}
handling.finally {
puts "Finally"
}

handling.go


% ruby test_flow.rb
Nested try
handling2 Caught exception: ERROR! in handling3
Hello
Finally
Uncaught exception: ERROR!

At first I tried to do it without continuations but I couldn't think
of a way to do so. Also the nesting is explicit which has the
advantage of not using global vars and the disadvantage of excessive
typing.


vanekl

2/8/2006 12:10:00 AM

0