No, really, everything is an expression

I just had a realization about Ruby that others coming from C, Perl, Javascript, PHP, etc. may not have quite grasped yet: In Ruby, since everything is an expression, semicolons are allowed within parentheses. That means parentheses effectively create a statement block (though not a new scope, and not a block in the Ruby sense of a first-class procedure object).

>> ("foo"; 42; :coolness)
=> :coolness

The above evaluates the values "foo" (a String), 42 (a Fixnum), and :final (a Symbol) in that order, and returns the last one. In Lisp, this would be a progn (Common Lisp) or begin (Scheme).

I've made use of this behavior before, I think, without really realizing the implications of it. In Perl, for example, you can use a bare block to group statements together. But you can't even apply a statement modifier to such a grouping. The following is a syntax error:

{             
  print "foo\n";
  exit(254);
} unless open(FOO, "foo.txt";

...leading to such idioms as:

print "foo\n" && exit(254) unless open(FOO, "foo.txt");

In Ruby, since everything is an expression, you can use statement modifiers on a parenthesized expression, like so:

>> (f = open("hello.c"); f.read) if File.exist? "hello.c"
=> # contents of hello.c

You could even use a compound expression as a conditional test (in a statement modifier!):

>> "non-empty file" if (f = open("hello.c"); !f.eof?)
=> "non-empty file"

About this entry