coderrr

May 20, 2008

rescue/ensure in all blocks

Filed under: ruby — Tags: — coderrr @ 3:26 am

One of the things I’ve wanted added to Ruby for a while was the ability to use rescue/else/ensure in all blocks without the need for begin/end.

For example:

Thread.new do |proc|
  begin
    instance_eval(&proc)
  rescue Exception => e
    handle_thread_exception e
  end
end

… could be written as:

Thread.new do |proc|
  instance_eval(&proc)
rescue Exception => e
  handle_thread_exception e
end

This saves us two (in my opinion unnecessary) lines and one indentation.

I started out by posting my Ruby 1.9 wishlist on the mailing list with this as my #1. At least a few people agreed and no one disagreed. So then I decided I’d ask Matz directly if we could have it. He promptly shut me down.

Matz thought that without the begin/end code it could be interpreted in two different ways (for blocks that are called multiple times).

foos.each do |foo|
  foo.bar
rescue BarError
  foo.baz
end

… could be interpreted as either:

# the correct way ...

foos.each do |foo|
  begin
    foo.bar
  rescue BarError
    foo.baz
  end
end

# or ...

begin
  foos.each do |foo|
    foo.bar
  end
rescue BarError
  foo.baz
end

I do agree that this looks somewhat ambiguous if you do not know the syntax. But, I’m not sure that outweighs the benefit of adding it to the language. Firstly, if a developer feels some code looks too ambiguous, they are free to use begin/end for clarity. Secondly, this ambiguity only occurs in blocks that are called multiple times (primarily iterators).

The second argument from Matz was about the confusion something like this could cause:

foo do
  bar
ensure
  baz
end

For example if the method foo never called the block then baz would never be called. Although someone could think it is called regardless.

# correct interpretation
foo do
  begin
    bar
  ensure
    baz
  end
end

# incorrect interpretation
begin
  foo do
    bar
  end
ensure
  baz
end

Again it’s up to the developer to use begin/end or not and I think that someone who knew the syntax would not misinterpret this code.

In the end I think Matz could be right, but I’m not sure. The core reason I wanted this added was I thought it would help make Ruby more concise (which is one of the reasons I love the language). But if it creates too much ambiguity that works against the conciseness.

I’m curious what other people’s take on this issue is. Feel free to comment.

Here’s a little script I wrote to find code that this could be applied to:

# findem.rb
ARGV.each do |fn|
  data = File.read(fn)
  ms = data.scan(%r{
            ( \n ([ \t]*) [^\s]+ \s+ do \s* (?:\| [^|]* \|)? \s*
                ( \n [ \t]+ ) begin
                  ( (?! \3 end) . )+
                \3 end \s*
              \2 end )
             }mx) rescue []

  if ! ms.empty?
    puts fn
    ms.each {|m| puts m }
  end
end

To use it: find ~ -name "*.rb" -print0 | xargs -0 ruby findem.rb | less. Replace ~ with whatever directory you want to search for ruby scripts in (for example the gems directory).

2 Comments »

  1. Regarding `find ~ -name “*.rb” | xargs ruby findem.rb | less`, please have a look at ‘Actions in bulk: xargs, -print0 and -exec +’ here:

    http://wooledge.org:8000/UsingFind

    Comment by Elias Pipping — July 11, 2008 @ 10:05 pm

  2. @elias: thanks for that, i’ve updated the post

    Comment by coderrr — July 11, 2008 @ 10:18 pm


RSS feed for comments on this post. TrackBack URI

Leave a comment

Blog at WordPress.com.