December 06, 2006
An Example of Ruby Introspection
In my blog post yesterday about Java, I made positive mention of the Ruby programming language. I pointed out that by giving you the power to do some potentially dangerous and confusing things, Ruby also gives you the power to do some very cool stuff. Let me give you an example.
Let's start with a class called Person with a single attribute name:
class Person
attr :name
end
Now, let's create an instance of this class:
someone = Person.new
Suppose later in the program you want to see if the person has been named. Here is a way to do it:
if someone.name.empty?
puts "no name"
end
That's simple enough, but probably wrong.
The problem is that if the name property has not been initialized, its value is nil and the NilClass does not have an empty? method. In this case the code will fail with an error:
NoMethodError: undefined method `empty?' for nil:NilClass
So the safe way to do this is to check if the value is nil before you check if it is empty. Like so:
if someone.name.nil? || someone.name.empty?
puts "no name"
end
This is great, except it gets very tedious and can be error prone. Testing for nil is sort of like checking return codes for function failures: it's a good thing to do but it's easy to forget and it adds a lot of crud to your code. The solution for the return code problem is to use exceptions. The solution to the nil problem is to use the power of Ruby introspection to make a small change to the way the NilClass works.
Ruby, unlike Java, lays its entire guts out for you to use and modify. So, I can go into the NilClass and give it an empty? method that works in a reasonable fashion.
class NilClass
def empty?
true
end
end
What we are saying here is that something that is nil should also be considered empty. Seems reasonable enough. Best of all, with this change the first test case—without the explicit test for a nil value—now works without error.
Redefining basic language constructs in this way is generally a very bad thing to do. I believe, however, you can break the rules if done in a judicious and considered manner. This is one case where the change is so simple to understand and benefit is so great, I think it's worth doing.
Here is what Dave Thomas says in the Pickaxe book:
One of the many advantages of dynamic languages such as Ruby is the ability to introspect—to examine aspects of the program from within the program itself. Java, for one, calls this feature reflection but Ruby's capabilities go beyond Java's.
I'd be curious if the Ruby practitioners would agree my example is a good use of introspection? And if so, where is your line on modifying system constructs?
Posted by chip
at 11:06 AM
to: Holidailies 2006, Software
Permalink
| Comments (6)
| Trackbacks (0)
Trackbacks have been closed on this entry.
I do like this particular 'fix' for NilClass #empty? It solves a slightly messy recurring issue with chaining method calls.
Unfortunately, it seems like confusion would arise, because Nil's so fundamental a concept in the language.
Posted by: Mars on December 6, 2006 02:17 PMI don't know what's all the fuzz this Ruby stuff but I use Visualbasic and can probably do more than string.empty? because in Visualbasic we can assign strings like mystr = "something" so it's not empty. Anyhow, why one would have an empty string?
Posted by: superdupont on December 6, 2006 07:14 PMI like the idea to add the empty? method to the NilClass.
There were too many times when I tried to remember, "Did I check my results right? Did I do the right thing?".
Don't get me wrong; I LOVE Ruby and friends and use it almost daily ... but that empty? or nil? drives me bananas at times.
I think the reason Ruby doesn't include something like this is that people don't use empty? in the first place. It's redundant. Instead of
if someone.name.empty?
puts "no name"
end
...just use...
if not name
puts 'no name'
end
Cheers
Posted by: Tony D on April 11, 2007 10:45 PMBTW - what you did isn't actually introspection. Introspection involves programmatically looking at the existing code (e.g. calling "methods"), not simply adding to it.
Hope you enjoy your programming...
Posted by: Tony on April 11, 2007 10:49 PMirb(main):006:0> name = ""
=> ""
irb(main):007:0> if not name ; puts "no name" ; end
=> nil
irb(main):008:0> if name.empty? ; puts "no name" ; end
no name
=> nil




