Submitted by why (Mon Mar 07 03:03:29 UTC 2005)
For many years, users have desired a means of testing for zero, nil and the empty string in a succinct manner. The cases often cited are:
1. The case in which a variable possibly contains a nil or an empty string. The current means of testing for this would be:
if var and var.empty?
Or, more commonly:
if var.to_s.empty?
2. The case in which a hash contains fields which should be skipped if they contain data (numerical or structural) that is ultimately valueless.
The verbose means of testing for this is to check for the key:
unless hsh.has_key?( 'key' ) and hsh['key'].zero?
The abbreviated form will first ignore nil (the default response for Hash#[]):
unless hsh['key'] and hsh['key'].zero?
This RCR has a dense history, a portion of which is listed below, based on discussion found on the Ruby-Talk mailing list.
Here are my requirements for a good solution:
Before defending my solution, let me first mention two other worthy answers to this problem.
The first involves creating a Boolean class which can be redefined, at the user's leisure, to include any values which the user wants to test as false.
One implementation of this, offered by Dan Berger, reads:
class Boolean def false nil or "" or 0 end end
This offers the ancillary benefit of giving 'true' and 'false' a mutual father, of which they are the only offspring.
However, this solution violates requirement #1. Users would be forced to choose between two Ruby environments. One of these Ruby environments would test false for 0, nil and "" BUT would break most of the assumptions taught in Ruby tutorials and manuals with concern to regexp matching in particular.
The second solution involves providing a method which casts object to their boolean equivalents. For example, the Integer class could be given a 'to_b' method:
class Integer def to_b zero? end end
Really, this method could be implemented in the same way Object#blank? is implemented below. It would just be named differently.
This solution offers a consistency with other type-casting mechanisms and may be even cleaner than the Object#blank? solution.
Were this to be implemented correctly, I would suggest:
I'm worried about #3 for this change, though. I think it might be a turn off to newcomers.
to_b? What's that? Short for "to boolean."
What's a boolean? Now we're having to introduce too many semantics just to get zero testing as false. And now we have more typecasting litering our code, which is what we're trying to get away from in Ruby.
I believe the concept of Object#blank? is easier to understand. It's simply a more comprehensive test rather than the normal test given by conditionals. A simple word, a simple change.
My only concern with Object#blank? is the possible confusion with String#empty? What's the difference between "blank" and "empty"? But I'd say we just scale back teaching of .empty? and it will settle in that "empty" implies a container, while "blank" implies a value in its most trivial state.
class Object def blank? if respond_to? :empty? empty? elsif respond_to? :zero? zero? else !self end end end
Comments | Current voting | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
|
RCRchive copyright © David Alan Black, 2003-2005.
Powered by .
I'd like to have something like the "eating nil" object that has been discussed on ruby-talk in order to solve the "problem" of use case 1 (if you want to make a problem out of it).
" ".blank? == false (!?)
I think 'blank' makes more sense than 'vapid' and so forth, but I tend to view the whole thing (perhaps idealistically) as a language-level workaround for things which should and can be worked around in other ways. It seems to me to carry the implication: Ruby involved a re-thinking of 0 and "" being false, and now we have to un-re-think.
I'm also not convinced by the implicit characterization of zero as the most trivial state an integer can be in.
(By the way I believe that this:
could just be this:
since nil and false respond to #zero?)
David Black
So you buy
nil#zero?
andfalse#zero?
, David?Whoops, never mind -- I got that wrong. -- DB
Sure, it's application specific, but there are a lot of applications it would simplify. If it doesn't fit your application, don't use it, use '.nil?' and friends.
If the idea is sound, but the set of things which could be considered blank is too application-specific, maybe have it take an optional parameter that was the set of things that should be considered to be blank?
That's how 'split' and friends work. They have default ideas of what you'd want to split on, but allow you to choose application-specific behaviour.
Maybe other languages make this an implicit feature so they don't have to come up with a name. Looking at the OED, I'd say that the best matching definition for "vapid" is "Devoid of animation, zest, or interest; dull, flat, lifeless, insipid", which the best matching definition for "blank" is "Void of result, unsuccessful, fruitless, nugatory; amounting to or producing nothing." I'd say that gives blank a bit of an edge over vapid, but at least nobody can claim that vapid will be misunderstood--most people will have to look it up.
I hope I'm not rehashing something, but I guess maybe 0 is a "degenerate" count, something like a line is a degenerate triangle. I'm really not advocating +degenerate?+, but maybe somebody can come up with an acceptable abreviation or synonym.
I actually like both approaches -- blank? and to_b.
To me to_b makes sense because it's consistent with the whole to_SOMETHING family of functions, quack.
Arguing that we have to teach newbies about boolean is somehow awkward - you still have to explain to them the whole "5" + "5" doesn't make "10" (as opposed to that language whose name we're not supposed to say ;) ), so why go nuts about having to explain concept of "boolean value"?
And unless they're completely new to programming (in which case I doubt they see 0 as false by default), they're already familiar with this boolean thing, aren't they?
On the other hand, I like blank? too. It makes sense to ask object whether he thinks he's "blank", except for some corner cases like " ", which is still "blank" string under some circumstances.
Just my thoughts, though.
Wejn
I agree that the semantic is rather application specific.
How about this:
#blank? or #vapid?, either way I'm happy :) --Gavin Kistner, feature whore
I would rather have the of the not of blank? - return nil if it is "blank" (nil, 0, 0.0, "", [], etc) and the value otherwise. If we call this "non_empty", you could do things like this:
I'd rather just have nil.empty? return true. This covers the common cases: 1. val is nil or a String 2. val is nil or an Array 3. val is nil or a Hash 4. hash[val] might or might not contain a String/Array/Hash
This is pretty similar to val.to_s.empty? since other cases mentioned (like 0, false, true) do not convert to the empty string; it saves the double-typing, or the cost of conversion to a string (large if we are talking about a big Array or Hash).
Corner case: [nil] converts to "". I would treat [nil] as not empty.
I don't see why 'empty?' can't be overloaded in this way (i.e. is it important that nil.empty? raise a method missing exception?)
However I would oppose nil.zero? (clearly nil isn't zero), and 0.empty?=true or false.empty?=true (there is a value there, not an absence of a value)