ruby picture

RCR 295: Object#blank?

Submitted by why (Mon Mar 07 03:03:29 UTC 2005)

Abstract

In conditionals, Ruby treats the number zero and empty strings as boolean true values. Object#blank? allows you to test for zero and the empty string, while providing a mechanism for discovering the most trivial value for any other class.

Problem

Use Cases

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?

History of the RCR

This RCR has a dense history, a portion of which is listed below, based on discussion found on the Ruby-Talk mailing list.

Proposal

I am proposing that one method, Object#blank?, be added to Ruby's Object class. This method would attempt to call the empty? and zero? methods, before implicitly typing the object as boolean.

Analysis

Requirements

Here are my requirements for a good solution:

  1. Must not change the Ruby's current dividing line between true and false. This means nil and false must remain the ONLY implicitly false values in Ruby. Thus, we preserve the use of String#=~, String#index, Array#index, String#gsub! and other methods which currently make this assumption.
  2. Must be named well. In the past, Object#empty? has been suggested. Some feel that empty? implies a container. A container which isn’t there for nil and 0. Other suggestions are vapid?, none? or mu?
  3. Must be useful to beginners. This could become a popular and very favorable addition to Ruby. Let's make it easy to teach and understand a learner's early years. (I see this being taught before .empty? or .zero?)

Other Solutions

Before defending my solution, let me first mention two other worthy answers to this problem.

Boolean Class

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.

Object#to_b

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.

Object#blank?

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.

Implementation

 class Object
   def blank?
     if respond_to? :empty?
       empty?
     elsif respond_to? :zero?
       zero?
     else
       !self
     end
   end
 end
ruby picture
Comments Current voting
The number(s) that should be treated as blank? (use case 2) is dependent on the *application logic*. There are many applications where zero should be this number, but there are others where this wouldn't be the case. So I'm against including application logic as a language feature.

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:

  unless hsh['key'] and hsh['key'].zero?

could just be this:

  unless hsh['key'].zero?

since nil and false respond to #zero?)

David Black


So you buy nil#zero? and false#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:

  class Object
    def in?( x );    x.include? self;  end
    def off?( x ); !(x.include? self); end
  end
  Blanks = [false, nil, 0, "", [], {}]
  if xx.off? Blanks then 
  if xx.in? Blanks then
  -- JeanHuguesRobert


#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:

 int = int.non_empty || -0.0001 # make 0/nil become 0.0001
 str = str.non_empty || "ERROR" # make ""/nil become "ERROR"


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

   if val and not val.empty? ---> unless val.empty?
   if hash[val] and not hash[val].empty?  ---> unless hash[val].empty?

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)


Strongly opposed 4
Opposed 4
Neutral 0
In favor 9
Strongly advocate 3
ruby picture
If you have registered at RCRchive, you may now sign in below. If you have not registered, you may sign up for a username and password. Registering enables you to submit new RCRs, and vote and leave comments on existing RCRs.
Your username:
Your password:

ruby picture

Powered by .