I was looking at the cvs changelog and my eye fell over
the ToDo file (btw, it seem it is not been updated in the last ten
months, but is still interesting
In the file there is an interesting reference to resumable exceptions.
As I recall, this was discussed here before and it was decided that it was a
bad idea.
The argument went something like this:
When you throw an exception, you do so to indicate some sort of error.
Generally, this means that execution after that point canât continue, so #resume doesnât make sense. The most you can do is retry, which starts
back at the beginning of the sequence of events to try the whole operation
over again.
I think the argument for it was that you could encounter a non-fatal error
condition, throw an exception to skip out, log the error, and skip back in
to continue down some alternate path. However, this isnât always possible,
and you have to specifically code for it.
So youâd have libraries that support resumable exceptions, and libraries
that donât, and trying to resume in one that doesnât causes big problems.
I think the general consensus was that Exceptions are supposed to
represent unrecoverable error conditions, so you couldnât resume. I
suppose you could implement some sort of recoverable exception using
continuations, but it might be ugly (actually, Iâll think about it and see
what I can come up with).
A rejected rcr about it. so I wonder what the final word is about
this.
I doubt it would be in 2.0, by following reason:
performance: resumable exceptions require âraiseâ to keep its
continuation inside of exception, that might slow down the
performance.
break assumption: although resumable exception is interesting
idea, I guess so many code assume âraiseâ not to resume.
can be done explicitly: we can resume to the throw point of the
exception by using âretryâ or âcallccâ explicitly. we donât need
to generalize resuming exceptions. NOTE: Iâm NOT the believer of
the âexplicit better than implicitâ principle though.
This probably isnât the safest way to do it, but it works. Note that if your
arguments to #raise are wrong, and it throws an ArgumentError, that
will get wrapped with a #resume method. Itâs probably safer try and
handle all the possible argument combinations to #raise properly, but
this way is a lot easier for a proof-of-concept.
Enjoy.
Dan
[dolio 11:35:18 ~] $ cat prac.rb
#!/usr/bin/ruby
def recoverable_raise(*args)
raise *args
rescue Exception => e
callcc do |cc|
scls = class << e; self; end
scls.send(:define_method, :resume, lambda { cc.call })
raise
end
end
begin
puts "foo"
recoverable_raise "baz"
puts "bar"
rescue Exception => e
p e
e.resume
end
[dolio 11:35:39 ~] $ ./prac.rb
foo
#<RuntimeError: baz>
bar
[dolio 11:36:00 ~] $
This probably isnât the safest way to do it, but it works. Note that if your
arguments to #raise are wrong, and it throws an ArgumentError, that
will get wrapped with a #resume method. Itâs probably safer try and
handle all the possible argument combinations to #raise properly, but
this way is a lot easier for a proof-of-concept.
Beautiful, Dan. And thus is the power of Ruby demonstrated once again.
Someone wants âraiseâ to be resumable, and it is eventually shown to be
possible using existing Ruby features.