b r a y d e n . o r g / Software

/ WebHome / LanguagePages / RubyLanguage / RubyArticleSDMag200212

This Web


WebHome  
Topic List  
Web Statistics 

All Webs


Books
Main
Random
Software
TWiki  

brayden.org


Home
Monthly Digest
Today's Links
Resumé
Reading List
Books RSS
Random RSS
Software RSS

Other


Dale's Blog

currently-reading
TextDrive

A Joyful Gem

You may know a baker's dozen programming languages, but make room for one more: According to its inventor, Yukihiro Matsumoto, Ruby can put the fun back in your code.

By Rick Wayne

What, another language to learn? Why bone up on Ruby, when any programmer worth her debugger can name a dozen useful languages? Three answers: learning, fun and productivity. Listen to Yukihiro Matsumoto (a.k.a. "Matz"), Ruby's inventor: "To be honest, Ruby is not yet a 'job-making language.' But learning new programming languages teaches you new ideas." He points out that Ruby teaches you dynamic object-oriented programming, scripting in a pure OO language, and, perhaps most importantly, the joy of programming. In our profession, new ideas are at a premium, as is joy-and Matsumoto claims that Ruby can invigorate even the most jaded cube-dweller.

Joy ? Toy

But don't assume that joy equals toy-real engineers use Ruby for real projects, and their number is growing rapidly. Should you join them? It depends on what you do. Speed-crazed bit-bashers shoehorning MPEG codecs into 16K ROMs need not apply: High-level scripting languages remain slower and fatter than low-level compiled ones. And no one's likely to write an OS in Ruby anytime soon-Ruby itself, after all, is built on top of C. But for general-purpose work, the language is definitely worth a look.

For taxonomists, Ruby is a pure object-oriented interpreted scripting language with weak typing. It offers easy access to operating-system facilities, little to no boundary between compile time and runtime, and the Big Three of Objects: encapsulation, inheritance and polymorphism. "Pure" just means that all language elements are objects; object orientation is built in, rather than bolted on. Yet Ruby allows a procedural style suitable for quickly throwing together a one-off script, and in addition to the traditional interpreter, there's an interactive environment for incremental development.

Ruby effectively bridges the worlds of quickie scripting and real software engineering. Those "throwaway" programs have a habit of clinging stubbornly to life, evolving into major systems over time-and a strange attractor called "Big Ball of Mud" ensnares many of them. Ruby proponents claim to usually avoid it. Why? Well, for one thing, it's simple to refactor and test Ruby classes. There's no intervening compilation, and weak typing lets you change a class's interface without compiler natterings. The interactive environment even lets you incrementally add or replace class members on-the-fly.

Ruby shamelessly borrows good ideas from other languages. As in Smalltalk, everything's an object, whose type is determined purely by the messages it responds to. From LISP comes the idea of mixins to extend classes. Perl fans will recognize the regular-expression support, right down to "$_" and its friends. (I said it was shameless.) Java wonks will be pleased to see garbage collection, as well as useful built-in class libraries. C hackers can dive under the hood and extend the language for low-level programming. And, of course, the general intent-a scripting language for OO development-is certainly familiar to Python aficionados.

Traveling Light

But Ruby has a distinctive syntax and a "feel" all its own, carrying little syntactic baggage. The Principle of Least Surprise holds: Things just work the way you expect. Programmers who've switched say that they spend more time pleasing the customer, instead of the compiler. Dave Thomas, coauthor (with Andy Hunt) of The Pragmatic Programmer (Addison-Wesley, 1999) and Programming Ruby (Addison-Wesley, 2000), described a recent multiphase delivery project of eight weeks' duration that deployed 25,500 lines of Ruby source in roughly 300 classes-and that was on time, with no bugs in production.

"This was a serious rush job," he states, "with an absolute deadline. I honestly couldn't have done it in any other language I know (and I know a few)."

My initial Ruby experience bears this out. Our group's C library for access to our home-brewed weather database comprises about 2,000 lines. Two hours after I opened my laptop, Programming Ruby and a text editor, I had 156 lines of Ruby that could read the format, retrieve data and write it to relational tables; with a little refactoring, I'm still using that code today.

Ruby's World

Let's take a look at some features and syntax examples that demonstrate what's different about Ruby.

Callbacks and coroutines crop up all over. In the Visitor design pattern, users of a collection can visit each item in turn, doing "X" with each. Event handlers use callbacks, too: "Do X whenever there's a mouse click on me." In C, you'd use function pointers; in Java, you'd use inner classes or design a pair of cooperating classes. Ruby implements callbacks with code blocks, pieces of code contained between curly braces or the keywords do and end. Code blocks can be passed to a method; inside the method, the yield keyword executes the code block as if it were inlined. If yield has parameters, these get passed to the block.

Doesn't sound like much? Wait till you see blocks in action. For example, to initialize, utilize and dispose of a resource, you might see Java boilerplate like:

File aFile = new File("input.txt");
  BufferedReader rdr =
   new BufferedReader(new FileReader(aFile));
  handleLines(aFile); // process all the lines
  aStream.close();
Ruby internalizes the boilerplate. Its File.open() method can accept a block of code. Deep down, open() yields to the block; after it runs, control passes back to open(), which closes the file.

Ignoring exception processing (which adds 4-5 lines to each example), the Ruby syntax is a one-liner:

File.open("input.txt") { |aFile| handleLines(aFile) }

Ruby also implements iterators with code blocks, which leads to a natural style that's brief and readable. Let's flesh out our example a bit, replacing handleLines with code that prints each line to standard output. We'll exploit Ruby's File.each_line() method to iterate over the file, and include exception processing, too. No loops, return values or end conditions:

begin
     File.open("input.txt") do |aFile|
        aFile.each_line {|aString| puts(aString)}
     end
  rescue SystemCallError
     handleException # defined elsewhere
  end
A world-beater? Probably not. Still, it's more straightforward than the 13-line Java equivalent. I'm not just whaling on Java's class library, here; it's no coincidence that Ruby's File provides the useful open() method and the each_line() iterator. Rather, the availability of blocks makes it so simple to build iterators and suchlike that when you need them, you'll find them-even in your own classes. In fact, you can exploit existing code in your classes, too, via Ruby's analogue to multiple inheritance. Although Ruby is a single-inheritance language, mixins can give a class behavior from more than just one ancestor.

For example, suppose you have a Programmer class that needs to support comparison operators based on average number of defects per line of code (for example, rick > finn). All that Programmer has to do is implement the comparison method <=> (returning -1, 0 or 1 for less than, equals or greater than), and you can mix in Ruby's Comparable module. Now your class suddenly sports the operators <, <=, =, > and >. If you have an aggregate class Team for groups of programmers and build an each method for visiting each in turn, you can mix in the Enumerable module-now Team magically has methods for searching and sorting. To find objects matching a pattern, use grep; find_all returns an array of objects matching a criterion (which you pass in via yet another block), sort returns an array of Programmer objects sorted on defects-per-line-there's more, but you get the idea.

Duck Typing

If you were raised on strongly typed languages like Pascal, C or Java, you probably believe that I'm trying to pull a fast one. "Wait a minute, just how does Enumerable even know that Team has an each method?" Well, it doesn't until runtime; enter Duck Typing. (Dave Thomas: "If it looks like a duck and quacks like a duck .") In other words, an object's type depends only on the messages it responds to; the determination is made at runtime, when the method is called. If it exists, all goes swimmingly; if not, an exception is thrown.

It sounds dangerous to us strongly typed types: We're used to a Compiler Cop standing between anarchy and us. But in my experience, Duck Typing just works; perhaps since it's so painless to run bits of scripting-language code, the bugs tend to get worked out early.

You can also modify classes at runtime, enabling introspection and "metaprogramming." The latter involves first building infrastructure methods that modify classes; those can then assume much of the scut work. For example, to use a persistence engine, you might build a method addPersist("foo", Integer) that would add an integer member "foo" to the class and set up storage and accessor methods in a single stroke.

What's Not to Like?

Ruby's not entirely wart-free. Matz admits that "Ruby's local scoping rule is too complex." And, "Some class libraries are not consistent with others. This is because they're not designed by me. I wish I had enough time, power and knowledge to implement them all in a consistent manner." Some love the power of redefining classes at runtime; others think that's dangerous: "Three things are most perilous: Connectors that corrode/Unproven algorithms/And self-modifying code." (from Duane Elms' song; "Threes, rev. 1.1, The Programmer's Anthem," 1988)

Weasel No More

Should you learn Ruby? If you have only compiled languages in your toolbox, definitely yes-scripting languages offer excellent bang for the buck. Ruby excels at "glue" code, pulling OS tools together with libraries from other languages. If you like Perl but would prefer cleaner object-orientation, Ruby is a shoo-in. If you're up-to-speed on Python, it's a harder question; I personally had an easier time learning Ruby, but at the end of the day, your productivity in each might not differ hugely.

Finally, don't think of time spent learning a language as time lost. Even if you go back to your standard, you'll be a better developer, aware of alternatives. That freshly aired mind will make you a clearer, more critical thinker-and you'll spend less time debugging!

Efficient, Invisible Fun

The buzz on Ruby

"The most important concepts in Ruby can be written in the letters FUN and JOY. Programming is supposed to be fun, but for various reasons, we often forget that. Ruby helps you to remember programming joy again."

-Yukihiro Matsumoto, Ruby creator

"Ruby works because it allows me to write code in terms of my user's problems, rather than in terms of language technicalities. This match leads to great efficiencies: When the client wants a change, the language they use to describe it is very similar to the code needed to implement it."

-Dave Thomas, author of The Pragmatic Programmer

"I'd been programming in Perl for years, and while I don't share the anti-Perl sentiments of a lot of Ruby programmers, once I saw Ruby and realized you could do all that cool stuff and have the code look so elegant and sleek, I was hooked. As I work on a Ruby program and get things more and more right, the code seems to disappear gradually from the screen."

-David Alan Black, Associate Professor of Communication at Seton Hall University

Supporting Cast

Ruby downloads, libraries and projects

The Ruby source distribution includes the interpreter, standard class library, interactive environment, debugger, profiler and Ruby syntax support for emacs, and requires a GNU-compatible C compiler. Prebuilt packages are also available for Windows and other OSes.

Ruby

Ruby Online: www.ruby-lang.org/en/

System Requirements:

Ruby is open source, and can be obtained for a number of platforms: various flavors of UNIX (including Linux), DOS, Windows 95/98/NT/2000/XP, Mac OSX, BeOS?, Amiga, Acorn Risc OB and OS/2. It ships with Red Hat and other Linux distributions, and with Jaguar (Mac OSX 10.2).

Rating: 4 stars The Rate Sheet Pros:

  1. Ruby has expressive, clean, readable syntax.
  2. It's scalable, suitable for quick scripts as well as sizable projects.
  3. It really is fun-things tend to work the first time.

Cons:

  1. I've been bitten myself by that local-scoping thing.
  2. Support and tool availability: You can still fit all the Ruby books in a backpack.
  3. The libraries are less extensive than Java's or Python's.

 
 
Current Rev: r1.2 - 25 Jun 2003 - 05:54 GMT - DaleBrayden, Revision History:Diffs | r1.2 | > | r1.1
© 2003-2011 by the contributing authors.