Monday, February 4, 2008

The Vague Syntax of Ruby and Ruby on Rails

I like the Ruby programming language a lot, and the Ruby on Rails web application framework is one of the best ones out there. One characteristic they share is an emphasis on writing concise code. Ruby pares down the syntax of writing object-oriented programs to a bare minimum. Rails emphasizes the use of a small number of conventions and idioms in order to avoid specification of all but the most essential details.

However, I think that both Ruby and Ruby on Rails take the principle of brevity to an unreasonable extreme. Here are a few examples.

First, Ruby (the language) does not require parentheses around conditions or method arguments. So, you can write
foo.bar baz, thud
instead of
foo.bar( baz, thud )
In the second form, isn't it much more obvious that we're calling a method, and that baz and thud are the arguments?

As an even simpler (and more ambiguous) example, say that you see this code in a Ruby method:
blat
A bare identifier does not really provide any clue that would suggest to the reader how the identifier is being used. In this case, it will be interpreted as a method call with no arguments. Wouldn't it be much more clear like this?
blat()
I think the general lack of visual cues in Ruby code makes it difficult to read.

Rails code (at least in the books and on-line tutorials I have read) tends to opt for the same kind of extreme brevity. For example, consider the following code:
redirect_to :action => :login, \
:destination => request.request_uri \
and return false
I found this code in an implementation of user authentication using something called Confluence4r. The code specifies what should happen when a privileged action is attempted without the proper credentials being present in the user's session. It's reasonably clear that a request is being redirected. However, an options hash is being used to specify the details of the redirection.

I guess that options hashes are good in the sense that unnecessary information can be omitted. However, I think options hashes are overused in Rails. An options hash is basically a "magic bag of goodies" that a method will use to carry out some behavior. However, the specification of the options hash at the call site does very little to inform the reader how the contents of the hash will influence the behavior of the called method. In the case above, it's reasonably clear that :action => :login will redirect to the login action. However, what is going on the :destination key? As far as I can tell, it simply puts request.request_uri in the query parameters of the redirected request, but I fail to see how that behavior is even hinted at in the text of the method call. Wouldn't something like the following be much clearer?
next_request = Request.new()
next_request,set_action( :login )
next_request.add_param( :destination, request.request_uri() )
redirect_to( next_request )
return false
Sure, we replaced 1 line of code with 5, but the reader would have a much better chance of figuring out what is going on.

Sacrificing a bit of brevity in order to get self-documenting code seems like a good tradeoff to me.

No comments: