On 2/23/09, Gavin Sinclair <> wrote:
> Hi,
>
> So far as the search engine on this newsgroup is concerned, there's
> been no discussion about including a Common Lisp-style condition
> system in a future version of Ruby. For information about these, [1]
> is a good readable resource.
>
> For those who don't know what they offer above normal exception-
> handling (as we know it in Ruby, Java, C++, ...), conditions need not
> be errors and they need not unwind the stack. A low-level function
> can signal that something is wrong, and provide multiple ways of
> handling it (which is appropriate, because the handling code is low-
> level). A high-level function can receive the signal and choose which
> low-level handler to run, or can "handle" it itself or ignore it (the
> latter two options being what's available in Ruby etc.). Another good
> example is low-level code issuing a warning and higher-level code
> deciding whether to print it. It's _very_ elegant.
>
> For those who _do_ know about Common Lisp conditions, is there any
> appetite for them in Ruby? I see great positives and no negatives.
> They don't seem at all un-Ruby-like; in fact the relationship between
> high- and low-level code reminds me of the elegance of blocks and
> yield.
>
> After a small amount of thought, I haven't come up with a suitable
> syntax. That's probably because I haven't used the things, only read
> about them.
>
> Just curious about people's thoughts, and quietly hopeful for Ruby
> 2.0 
>
> Gavin
>
> [1]
> http://gigamonkeys.com/book/beyond-e...-restarts.html
>
>
Exceptions don't need to be errors in Ruby, though conventionally they
are used for that and throw/catch for similar non-errors. As far as
the additional functionality of conditions, its not that hard to
implement at least what I see as the most important one (restarts) on
top of Ruby's existing exception-handling using continuations, e.g.,
in Ruby 1.8 (and, I would presume, Ruby 1.9 with the addition of
"require 'continuation'" at the top:
# This code is untested on any but the demo case at the end.
class Condition < Exception
FAIL = Object.new
def self.alert(*args)
block = (block_given? ? lambda { |c| yield c } : nil)
callcc { |cont| raise self.new(cont, block, *args) }
end
def initialize(c, block, *args)
@continuation = c
@restarts = {}
block.call(self) if block
end
def add_restart(restart_name, &block)
@restarts[restart_name] = lambda do |*args|
result=block.call(self, *args)
@continuation.call(*result) unless result==FAIL
nil
end
self
end
def restarts
@restarts.dup
end
def restart(restart_name, *args)
@restarts[restart_name].call(*args) if @restarts.include? restart_name
end
end
class BadValueCondition < Condition
attr_reader :value
def initialize(c, block, val, *args)
@value = val
super
end
end
def process_value(val)
if (val.div 2) == (val.quo 2)
BadValueCondition.alert(val)
else
val.to_s
end
end
def process(range)
range.map { |val| process_value(val) }.compact
rescue BadValueCondition => c
c.add_restart(:ignore) {}
c.add_restart(:substitute_warning) { |c, *args| "Bad: #{c.value}" }
raise
end
[:ignore, :substitute_warning].each do |strategy|
begin
puts "\n---\nDemonstrating restart #{strategy.inspect}"
arr = process(1..10)
puts arr.join("\n")
rescue BadValueCondition => c
c.restart(strategy)
raise
end
end