On 27/09/2011 19:46, Aaron Patterson wrote:
> On Tue, Sep 27, 2011 at 06:18:19PM +0900, Alex Young wrote:
>>
>> Issue #5372 has been reported by Alex Young.
>>
>> ----------------------------------------
>> Feature #5372: Promote blank? to a core protocol
>> http://redmine.ruby-lang.org/issues/5372
>>
>> Author: Alex Young
>> Status: Open
>> Priority: Normal
>> Assignee:
>> Category: core
>> Target version: 1.9.4
>>
>>
>> I don't think there's been a project I've used that hasn't made use of this pattern:
>>
>>    if thing.nil? || thing.empty?
>>
>> somewhere in its source.  This is necessary because it is idiomatic to return nil where other languages might return a null object, and there is no universal test for nullity which a user-implemented class can provide without issues.
>>
>> Facets (and ActiveSupport) define a #blank? protocol which allows for the above to be replaced with:
>>
>>    if thing.blank?
>>
>> Being able to type this on a first iteration saves forgetting one of the cases and having to come back to fix a bug later.  For projects where I cannot directly use Facets or ActiveSupport, I always find that I rewrite a version for myself.  It would be very convenient not to have to do this every time, and this is clearly a common case, so I propose that #blank? be implemented on the following classes:
>>
>>    Object: to return false
>>    String: aliased to #empty?
>>    NilClass: to return true
>>    TrueClass: to return false
>>    FalseClass: to return true
>>    Array: aliased to #empty?
>>    Hash: aliased to #empty?
>>    Fiber: to return !alive?
>>    Numeric: aliased to #zero?
>>    IO: aliased to #closed?
>>    MatchData: to return #to_s.blank?
>>    Process::Status: aliased to #exited?
>>    Range: to return self.include?(self.begin)
>>    Struct: subclass instances to return values.blank?
>>    Thread: to return !alive?
>>    ThreadGroup: to return list.blank?
>>
>> Some of these uses aren't described by the word "blank?" very well (and ActiveSupport's String#blank? is somewhat different), so as a sub-feature I'd like to suggest "null?" as an alternative method name.
>>
>> Apologies if this has been proposed and rejected before, but a quick search of redmine didn't show anything relevant.
>
> "empty?" implies that you're performing the operation on a set.  Why
> would you treat these other objects as sets?  It doesn't make sense to
> me.

That's the other way around. I'm not suggesting that the others are 
sets. I'm saying that for Arrays, Strings and Hashes, #empty? expresses 
the idea that these are null objects: they have no content, and 
operations on them are commonly no-ops.  I'm then generalising that and 
saying that this idea of there being a null instance of a class is 
applicable to more cases than just those classes which can be treated as 
sets.

>
> Not to mention defining the method on every possible object seems bad.
> What if my function shouldn't be dealing with Thread objects?  I'd rather
> a NoMethodError be raised.
>
We already have nil?, to_s and so forth which are defined everywhere. 
I'm suggesting an new protocol on that level, one which a user-defined 
class can participate in.

Think of it like the Null Object pattern in reverse. Because the core 
API commonly returns nil in error cases rather than having methods with 
a single return type, we can't blindly use the returned instance without 
a couple of type checks.  Instead, we need to ask each instance "are you 
null?" before operating on it.  The blank? method contains the logic 
which would otherwise have been used when deciding to build a Null 
Object, if that was the idiom.

This becomes more useful when you have:

     module Blank
       def Blank.===(x)
         x.blank?
       end
     end

Because then you can do this:

     case thingy
     when Blank
       # catch-all
     # other cases
     end

which I quite like.

-- 
Alex