Ruby is designed for language construction. But the language you create isn't limited to the features you think to build in. Instead, any language you build is an extension of Ruby--so all of Ruby remains at your disposal. Much of Ruby's power comes from its totally dynamic nature. It's expressiveness comes from its convenient syntax for the kind of code you generally need to write. As with any powerful tool, there are risks and complexities. But those issues can be managed. When you do, Ruby's value proposition is undeniable.
When I first took a look at Ruby, it seened too much like Perl--too many ways to do things, which would make it difficult to learn, because everything you read would look subtly different. Then I read Martin Fowler's article on Rake. That article described the advantages of having a build language that was effectively embeded inside of a general-purpose programming language. That prompted me to take a second look.
When I began looking at Ruby more closely, I found a wealth of capabilities, mostly stemming from the fact that Ruby is totally dynamic. Since everything is loaded at runtime, you can extend things in ways you really can't in most other languages, except for languages like Lisp and SmallTalk. (In fact, it is those aspects of the language that are the hardest to grasp for old-school programmers like myself. Making it a totally object-oriented language helps to manage that complexity, but there are still several new ideas to learn.)
As I began to learn more about Ruby, I found that I could do pretty much anything I wanted in only a few lines of code. At that point, might take 3 or 4 hours to figure out what those lines of code ought to be, but it was clear that once I had mastered the right techniques, I was going to have a very powerful language, indeed.
At this point, Ruby looked like a good choice for writing simple utility scripts. But it was also clear that it was being to used write some fairly large-scale applications. That was an appealing range of capabilities that combined the immediacy and fast cycling of script development with a capacity to seamlessly grow those scripts into a full-blown application--or at the very least, to use the knowledge gained in the scripting process. That was appealing because it meant that instead of having 3 languages (shell scripts for simple tasks, Perl for complex ones, and Java for applications), I might get down to one language, so that I had one large skill set instead of three very fragmented ones.
That was an appealing notion that got me interested in learning more. Then I went to a two-day Rails conference in Santa Clara sponsored by the Software Development Forum in 2006. What I learned there blew me away.
In that conference, a small parade of Ruby gurus passed before my eyes. They described a variety of best-practices I was familiar with. They had cool demos and great stories to tell. But the meta-message that came through loud and clear, after hearing all of the talks, was this:
Ruby is Designed for Language Construction
Not since Forth, in fact, had I seen a language that was more purposely tailored to that task. Like Forth, it was easy to "add words" to extend the language. But unlike Forth, it was fully object-oriented, with a pleasant syntax. (Back when I was coding Forth, I used to wish for a Forth with C-style syntax. At the time, I didn't even know enough to wish for OO capabilities. Little did I know that I would eventually see such a language, and that it would be called Ruby.)
A language you construct to solve a particular problem is known as a DSL, which stands for "Domain Specific Language". But after the conference, I began to think of it as "Design Solution Language". Even that term fails to arrive at the heart of the issue, however, because in reality Ruby lets you create a domain-specific extension to the Ruby language. It is there that real power of Ruby lies. When you create a language that helps you tackle problems in a given domain, you don't leave normal Ruby behind. You extend and augment Ruby to make things easier, but you always have at your disposal the power of the general-purpose Ruby language.
Several examples of that power already exist:
In all three cases, the languages make it a world easier for programmers to carry out common tasks. But they don't replace the implementation language (Ruby), they add to it. That's what makes Ruby different from a 4GL, for example, or other such languages where it is easy to do common tasks, but impossible to do anything else. You can always do what you could do before. You just have easier ways to do the things you do a lot.
Note: I owe Chiaroscuro for helping to plant this idea in my head. Here's my summary of his concept:
Liquid Development meta programming is about dealing with software design as language design. It's goal is to create an executable knowledge model that an experienced user can understand and manipulate. The first step in that process is to excavate the principles that underly the model, creating an ontology. The second step is to construct an executable version of the model, in the form of a language. Because Ruby excels at language-construction and at creating programs "on the fly", it is ideal for that purpose.
After that conference, I realized that a new design model may be about to emerge. In that model, you work with the intention of building a language that makes it easy to solve your problem. If you're a designer, you'll think in terms of designing that language. If you're an agile developer, you'll think in terms of building up to it, letting the language emerge from the use cases, as you implement them. But either way, any complex problem will tend to produce a language that makes it easier to accomodate changing requirements and solve future problems.
In that paradigm, a cadre of "hard core" Ruby coders will build and maintain the design language. They'll know all the ins and outs of Ruby's dynamic extensibility, and they'll deal with the complexities that come about as a result. But there can then be a much larger body of domain programmers who use that language to solve specific problems. Those coders won't need anything like the skill set of the core programmers, but they'll be able to do bigger and better things, the more Ruby they know.
As was pointed out at the conference, it's easier to read a foreign language than it is to speak it. The same holds true for a DSLs, as well. So the experienced Ruby practioners observed that a domain expert could review the code they had written for accuracy. Even when they didn't know Ruby they could follow the logic of a program written in a DSL, because that language uses terms that are specific to their domain.
In a similar vein, I have noticed that the scripts I have written have fewer comments--because the code is pretty darn readable. That's a novel experience for me.
The new design paradigm is poised to give rise to potentially new social construct--a continuum of coding expertise:
So in the same way that Ruby gives me a language that range the continuum of simple scripts, complex scripts, and applications, DSL extensions to Ruby creates a continuum of language skills ranging from simple domain-specific tasks, complex tasks, and the DSL itself.
Ruby's language-extensibility is made possible by the fact that Ruby is a totally dynamic language. Everything happens at runtime. There is power in that capability--but there is also risk. This section takes a look at both.
The fact that Ruby is totally dynamic makes it very easy to write. You can get a lot done in a few lines. And when you take advantage of its dynamic extensibility and evaluations, you can do some very powerful things in some very elegant ways. But that power comes at an expense: Ruby code can be harder to read, which can make it a lot harder to maintain.
When you're coding, and you want to know what an object is capable of, it can be difficult to find out. The object's class could be generated dynamically. It could be extended at runtime. Or the object might have been extended in ways that don't apply to the class as a whole. Those capabilities tend to make systems that are dearly loved and easily manipulated by those who built them, but which may be close to impenetrable by outsiders.
There are several strategies for dealing with that complexity:
Ruby doesn't require you to specify types, although you can. Perhaps more importantly, it uses duck typing. (If it quacks like a duck, it must be a duck.) In other words, if an object responds to the appropriate messages (methods) in the appropriate way, then it can be used in a similar context. So it may be possible to use either a Boat or Car in a context that requires a drivable object without having to create a Drivable interface or Vehicle superclass.
You can create a superclass if you want, of course, because Ruby is fully object-oriented. I've even seen an article on the web that tells how to construct an interface in Ruby. But you don't have to. That gives you a lot of freedom.
Here are some of the language features that make it so easy to express your intentions in Ruby:
To summarize, Ruby is useful for a wide range of tasks by itself:
But with the appropriate extensions, it's scope of applicability widens to include:
And that's just what's available today. Who knows what's coming?
Ruby is an extremely powerful, dynamic language. The more you know, the easier it is to create readable, virtually self-documenting programs. It can also be dangerous. There are a variety of strategies to manage the complexity. Using them minimizes the risks and maximizes the benefits of using Ruby. (To learn more, see Getting Started in Ruby.)
§ Home · Books · Health · Music · Dance · Golf · Yoga · Essays · Store §
Copyright © 2006
by Eric Armstrong. All rights reserved.
Contact me to send feedback, make a donation, or find ways to help others.
And by all means, be sure to visit The TreeLight Store.