Sponsored Link •
Yukihiro Matsumoto, the creator of the Ruby programming language, talks with Bill Venners about morphing interfaces, using mix-ins, and the productivity benefits of being concise in Ruby.
Yukihiro Matsumoto, or "Matz," as he is known online, is the creator of the Ruby programming language. Ruby is an object-oriented language suitable for writing day to day scripts as well as full-scale applications. Matz began work on Ruby back in 1993, because he wanted a language that made him productive while being fun to use. Initially popular in Japan, Ruby has been finding its way into the hearts of programmers all over the world.
On September 24, 2003, Bill Venners met with Yukihiro Matsumoto at the JAOO conference in Aarhus, Denmark. In this interview, which is being published in multiple installments on Artima.com, Yukihiro Matsumoto discusses Ruby's design philosopy, the features of the Ruby language, and becoming a better programmer. In this initial installment, Matz waxes philosophic about design imperfection, the danger of orthogonality, granting freedom with guidance, the principle of least surprise, and the importance of the human in computer endeavors.
Bill Venners: In Ruby, I can add methods and variables to objects at runtime. I have certainly added methods and variables to classes, which become objects at runtime, in other languages. But in Java, for example, once a class is loaded or an object is instantiated, its interface stays the same. Allowing the interface to change at runtime seems a bit scary to me. I'm curious how I would use that feature in Ruby. What's the benefit of being able to add methods at runtime?
Yukihiro Matsumoto: First of all, you don't have to use that feature. The most useful application of dynamic features, such as adding methods to objects, is meta-programming. Such features allow you to create a library that adapts to the environment, but they are not for casual uses.
Bill Venners: What's an example of a library that adapts to the environment?
Yukihiro Matsumoto: One example is a proxy. Instead of designing individual proxy classes for each particular class, in Ruby you can create an all purpose proxy that can wrap any object. The proxy can probe the object inside of it and just morph into the proxy for that object. The proxy can add methods to itself so it has the same interface as the wrapped object, and each of those methods can delegate to the corresponding method in the wrapped object. So an all-purpose proxy, which can be used to wrap any object, is an example of how a library class can adapt to the environment.
Adding methods to objects can also be used in Ruby in
situations where Java programmers use inner classes. For example,
if you need to pass a listener object to some method, in Java you
often instantiate an anonymous inner class that defines the
listener methods and pass it. In Ruby, you can just create a
plain object—an instance of class
needed listener methods to it dynamically, and pass it.
Also, because you can replace a method as well as add one, you
can use this feature to define callbacks. For example, every OO
GUI library has a
Button object. When someone clicks
a button, the
Button's click method is called. In
Button class, of course, the
implementation of the click method is empty. To add behavior to
the button in other languages, you create a subclass of
Button and override the click method. But in Ruby,
if you want, you can directly replace the click method in class
Bill Venners: In an article, you wrote, "Ruby supports single inheritance only, which I consider a feature." Why is single-inheritance desirable, and what are mix-ins?
Yukihiro Matsumoto: Single inheritance is
good because the whole class inheritance structure forms a single
tree with a single root, named
Object, and that is
very easy to understand. In languages with multiple inheritance,
the classes form a network, which is harder to understand.
But even though single inheritance is good because of its simple tree structure, sometimes we want to share features or methods between classes outside the lines of single inheritance. In that case in Java, you have to define an interface to share method signatures, then usually implement using some kind of delegation from one object to another. In Ruby, we define something called a module, which is like a class, but with some restrictions: namely, you can't create an instance of a module, and a module can't inherit from a class. So in Ruby, if you define a module that has methods and plug that module into a class, then that class will have those methods, both signatures and implementations. If you then plug the same module into another class, there will be two classes that share the implementation. This gives much of the benefit of multiple inheritance, but without destroying the simple tree model of single inheritance.
This approach of plugging in modules is called Mix-ins. Mix-ins originally started in LISP culture as a usage of multiple inheritance. In fact, a mix-in is actually a strict way of using multiple inheritance. So in LISP, it's a style of using multiple inheritance. In Ruby, we force you to use mix-ins by supporting classes and modules.
Bill Venners: In an article, you wrote, "Ruby's primary focus is productivity." How does Ruby help productivity, and what about efficiency and robustness? Productivity, efficiency, and robustness are all good things. How do you trade those off in Ruby?
Yukihiro Matsumoto: In my point of view, efficiency and productivity are the same thing, because we focus on programming efficiency, not runtime efficiency. I don't care about runtime efficiency. The computer goes faster and faster every year, so I don't really care about performance. Actually, I care about performance when I'm implementing the Ruby interpreter, but not when I'm designing Ruby. The implementer should care about performance, but the designer shouldn't. If you care about performance, you will focus on machines in design instead of on humans. So you shouldn't focus on performance in design.
To help programmers be productive, I focus on making programs succinct in Ruby. I try to make programs compact. I also focus on providing very rich features in class libraries. If you have a library method to do the things that you want to do, you don't need to write much code. In Ruby, we also have blocks, which are nameless functions you can pass to methods. So we can provide in libraries templates of common programming patterns, and you can provide a block to customize the pattern. Blocks are built into the Ruby language, so you can use them very easily. So by providing this rich set of features in the libraries, we help make Ruby programs compact.
As an example, Perl programmers have a technique called the
Schwartzian transform, named after Randall Schwartz, who
created the technique. You can use the Schwartzian transform if
you have a list that you want to sort in an order determined by
the result of passing each element of the list to a particular
function. For example, you may want to sort a list of filenames
in order of increasing timestamp. The list you want to sort are
filename strings, but the order you want them sorted in is
determined by passing each string to a function that returns a
timestamp for that filename. A Perl user would know this is a job
for a Schwartzian transform. They would pick up a book and look
at a Schwartzian transform example, then apply the sample code to
their particular case. But this is kind of a heavy activity for
your brain. In Ruby, we have a
sort_by function to
which you can pass a list and a function. So to do a Schwartzian
transform in Ruby, you don't need to look it up in a reference,
you just call
sort_by. This is an example of
conciseness, and it is much easier.
Bill Venners: One of the arguments made by static typers in the static versus dynamic typing debate is that static compile-time type checking helps programmers make robust systems. What is Ruby's attitude about robustness?
Yukihiro Matsumoto: The Ruby language itself doesn't care about robustness. Actually, the implementation of the interpreter, which is written in C, should be robust. No Ruby code should crash the interpreter. So I try to make the interpreter robust, but the language itself in its design does not care about robustness for two reasons. First, you need to test the system anyway to be robust. So we encourage unit testing using a testing framework to help achieve robust systems. The second reason is that programs written in dynamic languages are very easy to run and check. So for day-to-day programs that aren't not as serious as enterprise systems, you don't have to be as robust. It's not worth the cost of declaring types, for example. And because there are so many dynamic checks in dynamic languages, in most cases something very terrible usually does not happen. For example, Ruby checks array boundaries, so you don't have buffer overwrites. Ruby doesn't provide direct address manipulations, so you can't crash the stack space. Therefore in Ruby, I want to enable programmers to get a program ready to test in a short time by making programmers productive.
Come back Monday, November 24 for part II of a conversation with Bjarne Stroustrup, the creator of C++. I am now staggering the publication of several interviews at once, to give the reader variety. The next installment of this interview with Yukihiro Matsumoto will appear in the near future. If you'd like to receive a brief weekly email announcing new articles at Artima.com, please subscribe to the Artima Newsletter.
Ruby in a
Nutshell, by Yukihiro Matsumoto, is available on Amazon.com
Ruby: A Pragmatic Programmer's Guide, by Dave Thomas and Andy
Hunt, is available on Amazon.com at:
The Ruby Programming Language, an introduction by Yukihiro
An Interview with the Creator of Ruby, by Bruce Stewart:
Interview with Ruby Create Y. Matsumoto, by S. Ibaraki: