Almost everything is an object (and everything is almost an object!)

This guest post is contributed by David A. Black, a Senior Developer with Cyrus Innovation, Inc. David has been programming in Ruby for ten years, and is the author of The Well-Grounded Rubyist (Manning, 2009). He’s one of the founding directors of Ruby Central, Inc., the parent organization of the International Ruby Conference (RubyConf). David is also one of the most experienced Ruby and Rails trainers in the the business. One of his current projects is The Compleat Rubyist, a training event taught by David, Gregory Brown, and Jeremy McAnally. David is a frequent invited speaker and keynoter at Ruby and Rails conferences, as well as users groups, in the U.S. and abroad.

David A.
Black

Almost everything is an object

(and everything is almost an object!)

You’ll sometimes hear the statement that Everything is an object in Ruby. But is that true?

It’s almost true, but not quite. No, this doesn’t mean we’ve caught Ruby doing something wrong! It’s just an opportunity to look more closely at how objects operate.

Here’s an example of something that isn’t an object: an argument list. Consider a method call like this:

create_person("David", "Black", "New Jersey")

Three strings are serving as method arguments, and those strings are objects. But the argument list itself is not an object. There’s no ArgumentList class. Moreover, it would be hard to imagine one. Consider this code:

arglist = ArgumentList.new  # no such thing; a thought experiment!
arglist << "David"
arglist << "Black"
arglist << "New Jersey"

Already there’s a problem. The << “operator” is actually a method. That means that the code we’ve got is equivalent to this:

arglist = ArgumentList.new
arglist.<<("David")
# etc.

So in order to use an ArgumentList object, we need to be able to construct… an argument list.

The way out of this circularity is to include something in the language that isn’t an object. Argument lists are part of the syntax of the language. They’re not objects.

In addition to syntactic constructs like argument lists, Ruby also includes non-objects in the form of keywords like if, class, alias, and begin, end, rescue. Some of these keywords provide control flow techniques; some, like alias, let you conduct business with the interpreter that affects the world in which your objects live. Non-object keywords help to create the framework inside of which your objects can come into being and do everything they have to do.

So not everything is Ruby is an object, and for good reason. But there’s a corollary to this point, and an important one: even though not everything is an object, everything does evaluate to an object.


New to Ruby? Join our online Core Ruby course. Read details here.


Everything (really, everything!) evaluates to an object

Every Ruby statement or expression evaluates to a single object. This is true not only of variables, object literals, and method calls, but of control-flow structures, keyword-based statements, class and method definitions, and everything else. The object a statement evaluates to may be nil, but it will always be something. Understanding this principle can help you make sense of things that happen in your code, and also provides some techniques you can take advantage of.

You can get lots of information about how statements evaluate to objects by using irb. irb is very literal-minded, and will always print out the value of the statement you type in. Sometimes the value is obvious; sometimes what irb shows you is instructive. Let’s start with an irb classic: puts.

>> puts "Hi"
Hi
=> nil

Note the nil at the end: that’s the return value from puts. After all, puts is a method, so it has to return something. As it happens, it always returns nil. The printing out of the string is a side-effect.

Knowing that puts returns nil will help you predict the result of evaluating this code:

["one", "two", "three"].map {|n| puts n.upcase }

It’s a common learner mistake to expect the result to be ["ONE", "TWO", "THREE"]. In fact, it’s [nil, nil, nil]. Each time through the block, the block evaluates to the return value of puts—namely, nil.

If every statement reduces to a single object, then the principle must apply to things you probably don’t usually think of in this way, such as class definitions. Sure enough:

>> class MyClass; end
=> nil

An empty class definition body evaluates to nil. A non-empty class definition body evaluates to the last expression evaluated inside it:

>> class AnotherClass
>>   2 + 3
>> end
=> 5

You can, if you want to, assign this value to a variable:

>> x = class C; "Hi!"; end
=> "Hi!"
>> puts x
Hi!
=> nil

It’s kind of silly to do that, usually. But there’s one very common idiom that uses this technique. Remember that inside a class body, self is the class object itself:

>> class Thing
>>   self
>> end
=> Thing

You’ll often see this fact used for the purpose of grabbing hold, in a variable, of an object’s singleton class:

>> t = Thing.new
=> #<Thing:0x18cf18>
>> singleton_class_of_t = class << t; self; end
=> #<Class:#<Thing:0x18cf18>>

We go into the singleton class using the class << t construct, evaluate self, and exit the class body. The result of this fishing expedition is that we’ve landed the singleton class itself and saved it to a variable. Now it’s possible to do things with the class that you can’t do as long as it remains anonymous, such as calling class_eval on it.

(Note that in Ruby 1.9.2 there’s a singleton_class method, so the above technique is probably going to start disappearing as more people move to 1.9.2 and higher versions.)

Most class bodies evaluate to nil because, typically, class definitions end with a method definition—and method definitions always evaluate to nil:

>> def my_method
>>   "Doesn't matter what I put here"
>> end
=> nil

Of course, when you call the method it returns a string:

>> my_method
=> "Doesn't matter what I put here"

But the method definition itself evaluates to nil.

Conditionals, including if statements and case statements, also always evaluate to an object. This isn’t exactly an obscure point; after all, the ultimate value of a conditional is often put to use.

>> action = "stop"
=> "stop"
>> message = if action == "stop" then "Bye!" else "Continue!" end
=> "Bye!"

There’s an interesting general rule, though: If no condition is true, or no case matched, then an if statement or case statement always evaluates to nil.

>> if 3 > 5
>>   "Universe is in trouble!"
>> end
=> nil

>> case 1 + 1
>> when 3
>>   "My world is upside-down!"
>> end
=> nil

One place you’ll see this put to use is in templating situations where someone wants to include something conditionally. Consider this erb fragment:

<%= "#{first} #{"#{middle} " if middle}#{last}" %>

The expression "#{middle }" if middle will evaluate to nil if middle is nil. Interpolating nil into a string is the same as interpolating an empty string, so that whole part of the larger string will simply be an empty string. If middle isn’t nil, then middle plus a space will be interpolated into the larger string. Mind you, if you actually use this technique, people might accuse you of having overly clever or “cute” code—and they may be right! But even if you don’t use it in real code, it’s an instructive example.

So even though not everything is an object in Ruby, an object is never very far away. Every statement eventually reduces to a single object. You might say that almost everything is an object—and everything is almost an object. Much of the time, the objects produced by control flow statements, class and method definitions, and keyword statements are ignored. But when you want or need them, they’re available. And exploring what statements evaluate to can teach you a lot about what Ruby is “thinking”.

Post supported by Zencoder Inc.: Zencoder does video encoding, as a service, in the cloud. Hook up to their API, and within minutes, you’ll be ready to process video, whether it’s 10,000 videos or just 10. Zencoder is highly optimized for speed, uses the best compression technology around, and handles hundreds of video/audio formats (including obscure and corrupt files). Also check out their open-source HTML5 video playerVideoJS.

Do read these awesome Guest Posts:

comments powered by Disqus