Summary
Developers new to Ruby tend to think that Ruby's conciseness is mostly due to an expressive core API. As Charles Nutter shows in a recent blog entry, however, Ruby's elegance is to a similar extent due to its flexible grammar, keeping high-level features, such as field-initializing constructors, void of syntactic sugar.
Advertisement
In a recent blog entry, Why Add Syntax When You Have Ruby?, Charles Nutter, one of the core JRuby developers shows how Ruby's flexible syntax makes syntactic sugar in API-level features unnecessary.
His example is a result of a discussion about Jython and Groovy syntax, which allow you to instantiate a JLabel as such:
JLabel(text, bounds = bounds, foreground = color)
This syntax is a shorthand in those languages to assign an array of instance variables to the specified set of values. Nutter notes in his blog that JRuby does not provide such a feature, and wonders what it would take to provide similar functionality in JRuby.
First, he notes that equivalent Ruby syntax would look like:
These versions use the Ruby new method, which is part of every Ruby class, to create a new instance of a class. In this case, text is a parameter defined in JLabel, and, in the second version, the following is a block that assigns the object's instance variables to the specified values.
While these would syntactically work in Ruby, Nutter notes that:
There's no existing accommodation in the semantics of Ruby for these syntax to work out of the box. It would not be hard to write a short bit of code to allow constructing objects in this fashion, of course (using the block approach as an example):
class Class
def construct(*baseargs, &initializer)
x = self.new(*baseargs)
x.instance_eval(&initializer) if initializer
x
end
end
The above code snippet passes an array argument (*baseargs), and a block argument (&initializer) to the construct method. The base arguments array is passed to the static new method, which is the Ruby way of creating a new instance of a class, and the &initializer block is subsequently evaluated for the newly created instance.
Nutter notes at this point that Ruby's grammar allows an even more concise definition. That's because a Ruby method can dynamically tell if a block was passed as one of the method's arguments. That's done through the block_given? method, which is part of the Kernel module (the Kernel module in Ruby is "mixed into" the Object class, making Kernel methods available anywhere in Ruby code).
In Ruby, the yield keyword will return the block passed into a given context, such as a method. Combining the block_given? kernel method with yield allows Nutter to define the above functionality in a simpler way:
class Class
def construct(*baseargs)
x = self.new(*baseargs)
yield.each_pair { |k,v| x.send("#{k}=", *v) } if block_given?
x
end
end
The above implementation also takes advantage of the send method, which invokes a method identified by x, passing that method the specified arguments.
Nutter concludes that the above implementation of construct would allow the following syntax in JRuby:
Here, the outer {} defines a block, and the inner pair of curly braces a hash of initializers.
Nutter concludes that:
The bottom line is that this kind of syntactic sugar in other languages can easily be added to Ruby through various techniques, and so features like field-initializing constructors don't need to be part of the core language or any of the implementations.
Nutter's blog post is a good demonstration of how relatively complex functionality can find a fairly simple expression in a combination of Ruby's syntax and APIs. JRuby promises to bring some of the expressive richness to Java as well. In what areas of your applications would you find this sort of expressivity most helpful?
Fun thing is, even Matz recognized that Ruby's lack of "true" keyword arguments (à la Python) and tentative emulation via the use of literral hashes was a kludge, which lead to the proposed inclusion of native keyword arguments in Ruby 2.0