From: Ben Giddings Date: 2004-04-07T03:06:08+09:00 Subject: Re: deciding between ruby and python Ville Vainio wrote: > Gavin> # Connection: the database connection is automatically released at > Gavin> # the end of the block, including if there is an exception therein. > > try - finally blocks are used for this in Python. > > Gavin> # Transaction support: any exception in this block will cause a > Gavin> # "ROLLBACK". If all is fine, a "COMMIT" is performed at the end. > > try: > stuff() > except: > rollback() > else: > commit() > > But I admit that the ruby approach seems cleaner here. It's probably > easier to use and the logic doesn't clutter the code. Not only that, but in Ruby you don't need to know about "rollback" or "commit". You also don't need to know what kinds of exceptions might be thrown. That, to me, is the enormous difference between using blocks and using exceptions. The person writing the transaction-type method knows best what might happen during the attempt. Let's take the database example: The database expert writes: def do_transaction(arg) try_number = 1 begin db = dbh.open(arg) yield(db) rescue ConnectionError => e # Ah, this type of error often happens when # the server is under heavy load, we should try again if try_number < MAX_TRIES tries += 1 retry end # Too many retries $stderr.puts "Too many retries!" rescue ExplosionError # Ah, this one is serious, we should probably # make a big deal out of it raise e rescue NoSuchDatabaseError # Oops, no such database, tell the user $stderr.puts "No such database!" ensure db.close end end The database user writes: do_transaction(arg) { |db| db.doStuff() } Here the person using the database doesn't need to know which exceptions are retryable, which are serious, etc. All they need to do is call it with a block. The person writing the database stuff will make sure that the appropriate thing happens at the appropriate time. Now, there are lots of ways of doing this in other languages, but Ruby is the first language I've seen that does it so easily, so commonly, and so often. I'm always amazed when I start tracing what really happens with certain block-using methods. What appears to be a simple call, and is so easy to use, actually does all kinds of things behind the scenes, often passing my block all around to different methods before actually yielding to it. I think the important thing isn't that this is possible in Ruby, but that it is a fundamental way in which the language is used. It's like recursion in functional languages. Many of them can do loops with iteration or recursion, and most imperative languages can do both as well. The real difference is that you'll see recursion all over the place in the functional languages, and it makes doing certain types of things much easier and more obvious. Ben