Friday, December 09, 2005

Ruby's Grand Variety

I'm starting to learn Ruby and it does seem as though it may fulfill its promise of "making programming fun again", but Ruby has a very unusual design philosophy. A lot of languages take the stance that on a syntactic level, and in the standard library, there should be One Way to do everything--one syntax, one idiom, one function.

Ruby says "to hell with that!" There are often several ways to do something, which should make programming comfortable to a variety of programmers, but I suspect it may be difficult to understand other people's code, if they use some of the more obscure features. Here are some of the duplicated language features you'll find in Ruby:
  • Two syntaxes for closure blocks, "{ ...code... }" and "do ...code... end"
  • Control structures before or after inner code, like in Perl: "x += 7 while (x % 13 != 0)" or "while (x % 13 != 0); x += 7; end"
  • You can choose betwen "and"/"&&", and "or"/"||". What's peculiar about these are their precedence rules: && has higher precedence than ||, and "and"/"or" have higher precedence than both of them. However, "and" and "or" have equal precedence. Similarly there are "!" and "not" operators.
  • The regular expression support in the language mimics Perl, offering such opaque variable names as $&, $', $` and $1. These variables are aliases for an object-oriented regular expression information bundle in a MatchData object called (you guessed it!) "$~".
  • Several syntaxes for strings, the most common being single-quoted and double-quoted, which have different syntax rules (but multiple string types is actually useful and common in new languages, although I'm a little disappointed that Ruby has no equivalent of C#'s @"literal\string")
  • There seem to be two exception-handling mechanisms, "raise...rescue" and "throw...catch". The relationship between them is not clear to me.
A more distressing aspect of Ruby's design is that it usually does not attempt to "save the programmer from himself": there are many mistakes you can make for which the interpreter will give no warning or error. Here are some examples.
  • 'Tuple assignments' don't have to be balanced. "a,b,c = 2,3,4" sets a=2, b=3, and c=4. But you can also write "a,b,c = 2,3" or "a,b = 2,3,4" or "a = 0,1,2". Can you guess the results?
  • Similary, the number of arguments to a closure doesn't have to match the number of arguments the closure accepts.
  • Problems caused by scoping rules. For one thing, assignable methods are normally invisible to members of the same class, so that you have to write " = 10" instead of "property = 10"; the latter unexpectedly creates a local variable called "property". For another thing (which Ruby's creator called "the single biggest design flaw in Ruby), the scoping rules for blocks are weird.
  • Because Ruby is dynamically typed and interpreted, most errors occur only at run-time and are not detected if the bad code is not executed, even when it would be theoretically possible. This, of course, is a vice of all dynamic interpreted languages.
Finally, Ruby has a number of features that can make it incomprehensible to programmers of other languages--rules that you won't find in many other languages. Here are some examples:
  • Return values are implicit. The value of the last statement in a method is the return value of the method. "if" statements and loops can also return values this way.
  • Hidden variables and state: I haven't quite figured out how this works yet, but the special variable $_ can be implicitly used for comparisons in control constructs (if, while, etc.), and range variables (and even range literals such as 1..10) seem to have an internal boolean state that can also be used implicitly.
  • Enforced naming conventions (whether an identifier starts with an uppercase letter affects its meaning; also, use @ to name member variables, @@ to name static (class) member variables, and $ to name globals)
  • In addition to the aforementioned regex variables, there are many other "punctuation variables" such as $@ and $/. The "$" indicates that they are global, but in fact, some of them are locally scoped variables.
  • "Parallel assignment" has a lot of rules and features.
  • Two operators ".." and "..." with slightly different meanings.
  • There are numerous string and pseudo-string types that begin with "%". For example, %x{blah} is equivalent to `blah`, and %Q!hi! is equivalent to "hi".
  • There are three different equality operators, ==, eql? and equal?, plus some "sort-of-equals" operators: === and =~.
  • Plenty of other stuff. Things are just done differently in Ruby.
By all of this, I don't mean to suggest that Ruby is "bad" (in fact, I think most of this stuff is mostly good), but I do think that if you're an experienced programmer, you should study Ruby explicitly before attempting to understand or write Ruby code, because your knowledge of other langauges might not be enough. Luckily, the book Programming Ruby is designed for you.

Incidentally, while you can write clear and readable code in Ruby, you can also obfuscate it nearly to the extent of C code. There should be a Ruby obfuscation contest...