Search This Blog

Saturday, August 30, 2008

An Introduction to Ruby - Technology

An Introduction to Ruby

Ruby is an object-oriented scripting language that combines the object-oriented programming (OOP) power of Smalltalk with the utility of Perl and the power of Python.

I can already hear you grumbling, "Oh, great, another language." Why should you care? You're already up to your elbows in technologies to learn, right? Well, call your significant other and tell him or her you're going to be late tonight. And go get another Jolt. You're going to be hooked on Ruby for the simple reason that Ruby makes programming fun again (and that's what really counts).

This article introduces Ruby by examining the high-level properties of the language as well as some important features that distinguish it from other languages. We'll compare Ruby with Perl and Python. Along the way, I'll provide my personal take on Ruby, and why I think you'll love it as much as I do.

Traits That Recommend Ruby

Looking down on Ruby from 50,000 feet, we see it has several qualities that recommend it as the next language you should learn, including its consistency and elegance, OOP features, ease of use, extensibility, and its applicability to rapid development.

But what exactly is Ruby? It's a weakly typed, interpreted, cross-platform language implemented in C, written from the ground up to be object-oriented. Ruby's been around since 1995, and has its largest following in Japan, the home of Ruby's author, Yukihiro Matsumoto, a.k.a. "Matz." Although Ruby is more popular in Japan than Python, you may not have heard much about it, yet. Or, if you have, you may have heard that it's a "better Perl than Perl."

I think there's a large measure of truth in that statement. Ruby somehow manages to combine some of the best features of Smalltalk, C, Perl, and Python in a package so elegant and natural that I found myself hooked after just one day.

Some of Ruby's strongest traits are:

  • Consistency. This is perhaps the single most important trait
    of Ruby. Like C, Ruby is a small language. It's a simple language,
    and Matz has made sure that a small set of rules govern its design. The result is Ruby stays out of your way; you don't have to build scaffolding to get it to actually work on the problem at hand.

  • Elegantly object oriented. Ruby fully supports object-oriented programming, with a from-the-ground-up, internally consistent and straightforward object model. Ruby has all the features you'd expect from an OOPL, including classes, methods, (single) inheritance, polymorphism, class methods, exceptions, and more. These features are designed from the beginning, not glued-on, which means Ruby doesn't have the little inconsistencies and gotchas you'll find elsewhere.

    For example, remember when you were learning C++ and the difference between pointer-to-function and pointer-to-member-function bit you on the butt? Or in Perl and Python, where the object (or reference, or string) is passed to your method as the first argument? Or the lack of access controls in Perl. Or.... Well, you get the idea.

  • Easy to use. Ruby's consistency and its object model make it easy to use. The syntax is simple, clean, and readable, with an expressiveness that gives your code a natural flow. Specific features of the language, such as the ability to pass code blocks on method invocations, make Ruby source wonderfully transparent. There's little of the "line noise" you find in other languages, and although there's also none of the bondage-and-discipline nature you'll find in other languages, Ruby seems to promote a sparse, readable, succinct coding style.

  • Easy to extend. Using its C-interface extension API, you can
    create interfaces from Ruby code to existing third-party commercial or
    noncommercial libraries, or selectively implement methods or classes in a lower-level programming language to improve performance.

  • Perfect for rapid development. Finally, because Ruby is an interpreted language, it is ideally suited to rapid application development. Write your code, run it, debug it, and run the modified code again--fast, simple, and fun. This low-torque, high-revolution
    implementation style lets you flow more easily from conception to incarnation as you code. The result can be a surprising increase in your productivity. Although Ruby can be used for large-scale, long-term projects, I now count it as my favorite language for prototyping and spike-solution development.

    These aspects of Ruby arise from the design of the language, its purpose, and its heritage. But aside from these more abstract positive qualities, there are several specific language features that you'll want to explore in Ruby. These include code blocks/closures, iterators, variable scoping by name, and its uniform object treatment

Sample Code and Output

Related Reference

Ruby in a Nutshell

By Yukihiro Matsumoto With translated text by David L. Reynolds Jr.

Table of Contents


Sample Excerpt

Full Description

In Ruby, a block is a group of statements appearing next to a method invocation; it is the last argument passed to the method. Sometimes this block is referred to as an "anonymous inline
callback." This code block isn't called in the lexical scope in
which it appears; rather, it can be called from the method that is being invoked (using 'yield'). It's a form of callback, but has greater power because it can be used as a closure. A closure is code that "closes off" or "packages up" references to variables outside
the lexical scope that exists at the time the code is
executed. In simpler terms, Ruby's blocks allow you to simply and expressively pass behavior as well as data in method invocations. A method invocation becomes a dialog between the caller and the callee.

def method_that_calls_a_block(arg)

puts "The argument to the method call was #{arg}."

# Here, the variable 'xyzzy' (which is used in the
# block below) is undefined; it's not in this lexical
# scope. But using a block, the code in the block
# can reference 'xyzzy'.

# Now we call the block using 'yield', passing it
# the square of the argument to this method.

yield arg * arg


# Our magic variable
xyzzy = 42

# Now we invoke method_that_calls_a_block, passing it
# 2 as an argument. The block is invoked from inside
# the method (above, using 'yield').

method_that_calls_a_block(2) { arg
puts "Hello from the block. Argument to the block is #{arg}."
puts "xyzzy is #{xyzzy}."
xyzzy += arg
puts "Hello again. Now xyzzy is #{xyzzy}."
puts "All done with the method. Now xyzzy is #{xyzzy}."

The output from this contrived little demonstration is:

The argument to the method call was 2.
Hello from the block. Argument to the block is 4.
xyzzy is 42.
Hello again. Now xyzzy is 46.
All done with the method. Now xyzzy is 46.

One of the most powerful, pervasive, and downright fun results of having blocks is Ruby's iterators. One of the nice things you'll find in most OOPLs is a set of classes for maintaining collections of objects. Yes, container classes certainly reduce drudgery, but there can be problems with them. Especially with designs that are a bit too "intimate." In Ruby, an iterator is a method implemented by a container using blocks as callbacks, which allows the iterator to
supply the knowledge about how to traverse the collection and the invoker to supply the knowledge about how to perform operations on elements in the collection. This simple abstraction allows an elegant decoupling of collections and their contents. For example, to sort an array of strings by length (instead of by alphabetical order):

goofyStuff = ["orange", "phase-plasma cannon", "epistle"]
p goofyStuff.sort # Default compare - alphabetical
p goofyStuff.sort { a,b a.length <=> b.length } # We specify compare

The results are:

["epistle", "orange", "phase-plasma cannon"]
["orange", "epistle", "phase-plasma cannon"]

In Ruby, variables are scoped by name:

  • Locals start with lowercase.
  • Instance variables are prefixed with '@'.
  • Class variables start with '@@'.
  • Constants start with an uppercase letter.
  • And finally, globals start with a dollar sign, '$'.

There is a certain resemblance to the "line noise" found in Perl, but in practice, the result is far more readable and intuitive. There's no variable declaration in Ruby, but with a glance, this scope-by-name feature allows you to tell what kind of variable you're looking at:


This lets you go a little crazy:

$xyzzy = @@xyzzy = @xyzzy = xyzzy = XYZZY

Lastly, in Ruby, everything is an object. The practical and positive results of this simple rule can't be overemphasized. There's a pure, intuitive joy to be found in calling methods on literals:

length = "Hello, world!".length
10.downto(1) { n puts n, "You're getting sleepier... " }

I can only look back and rue the days I spent writing Java code to flip-flop back and forth between the fundamental data type and the class for integers. Just say no, with Ruby.

Regarding Perl and Python

If you've gotten this far, Ruby probably sounds intriguing to you. But there's still that nagging question of "Is it better enough than Perl or Python to make it worth my while to learn it?" It's a valid question, and naturally, only you can answer it. But there are a few salient differences to consider.

Ruby owes much to Perl. It resembles Perl in many ways, sharing many shortcut globals (things like $0, $$, and so on), and it shares Perl's power to manipulate textual data. But Ruby is like a sleek, elegant Perl, with a fully integrated object model. It handles complex data structures better, and seems better suited to programming in the large. And, it's a heck of a lot easier to read.

My Perl background is quite a bit stronger than my experience with Python, and I found myself breathing an "Ahhhh" of relief with Ruby. There are some things that really bugged me about Perl. For instance, I have to agree with the sentiment that Perl seems to encourage a write-only coding style. It's just plain hard on the eyes. And there are some things that just make me shudder, like the lack of method access controls, and the lack of data inheritance.
But, these things seem to melt away with Ruby.

Ruby has similar goals to Python, but a significantly different approach to meeting those goals. Ruby differs in the unified nature of its object model; Python uses a procedural-and-object hybrid approach that at times feels a bit contrived. I find all those 'self' references tedious. In
Python, methods are objects, which they aren't in Ruby. Python supports multiple inheritance, which Ruby does not. On the other hand, Ruby also has much greater syntactic flexibility than Python. And Ruby makes superior use of blocks, closures, and iterators.

I think the biggest difference between Python and Ruby is philosophy. I suspect that folks will be drawn to one language or the other based on factors that are ineffable.

No comments: