Nil? with block


#1

If you have a long chain of method calls, but want to check some
temporary values for nils, for instance to raise an exception,
currently the easiest way is with local variables:

e.g.:

def zipcode_for_user(user)
address = user.personal_data.address
raise ‘no address!’ unless address
address.zipcode
end

If “nil?” is extended to accept a block, and to return self (for
Object) and the result of the yield (for NilClass), the following
notation becomes possible:

user.personal_data.address.nil?{ raise ‘no address!’}.zipcode

This is more concise, and (I think) also more readable. The flow of
the program isn’t broken by the nil-check. Apart from raise
statements, it would make sense to use return statements in the
blocks.

The implementation is trivial:
class Object ; def nil? ; block_given? ? self : false ; end ; end
class NilClass ; def nil? ; block_given? ? yield : true ; end ; end

Do you think this is a good idea?


#2

Hi –

On 3/21/07, Michiel de Mare removed_email_address@domain.invalid wrote:

end
blocks.

The implementation is trivial:
class Object ; def nil? ; block_given? ? self : false ; end ; end
class NilClass ; def nil? ; block_given? ? yield : true ; end ; end

Do you think this is a good idea?

No, I don’t like it. It’s unidiomatic for a ?-method to take a block,
and also for a block to serve as a conditional branch. I’d rather see
it unrolled. I understand the reasoning behind it, but it ends up
being idiosyncratic in its semantics.

David


#3

On 21.03.2007 13:51, Michiel de Mare wrote:

end

If “nil?” is extended to accept a block, and to return self (for
Object) and the result of the yield (for NilClass), the following
notation becomes possible:

user.personal_data.address.nil?{ raise ‘no address!’}.zipcode

This is more concise, and (I think) also more readable.

Actually I am not sure about readability. Lines tend to grow pretty
long that way. I would probably not use it. Also keep in mind that you
will usually get an exception anyway since you’re most likely calling a
method that nil does not support.

The flow of
the program isn’t broken by the nil-check. Apart from raise
statements, it would make sense to use return statements in the
blocks.

The implementation is trivial:

… and probably slower than the current implementation as they add
another boolean check.

class Object ; def nil? ; block_given? ? self : false ; end ; end
class NilClass ; def nil? ; block_given? ? yield : true ; end ; end

Do you think this is a good idea?

I am not so sure. But you can easily implement this with another method
name so you don’t have to change existing code’s behavior. Also, nil
values might be just only one reason to throw an exception so I am not
sure about general usefulness.

Yeah, I know - I’m a conservative skeptic. :slight_smile:

Kind regards

robert


#4

I am not so sure. But you can easily implement this with another method
name so you don’t have to change existing code’s behavior. Also, nil
values might be just only one reason to throw an exception so I am not
sure about general usefulness.

I also doubt this would ever be useful. I like ‘long lines’ and hate
loads of ‘if-statements’ but I must admit I find them useful (or at
least, any other solution looks awful to me).

Why not using, let’s call them “accessors?” which are “accessors with
a question mark”.
Just by hacking with a bit of “method_missing” in both your class and
NilClass, this would not slow exec time much I guess, and the
implementation should also be trivial.

  • in the class, you just send(accessor_name) when you get an unknown
    accessor_name?
  • in nilclass, you return “stuff” when having a ‘?’ method (except
    nil? or empty?)

The problem, IMHO, resides in the impossibility to return a coherent
“stuff”.
You want Ruby to NoMethodError you when you fail something.
You can’t always know what you could prefer when your call fail.
Should “stuff” be nil, 0, empty string, new object ?

I suggest you use that in your project (if that’s a big one) and then
tell us in a few weeks/months if that changed your life or not. If
that made your debugging harder.
Then again, this is a matter of personal preference I guess :wink:


#5

On Mar 21, 2007, at 9:04 AM, David A. Black wrote:

No, I don’t like it. It’s unidiomatic for a ?-method to take a block,

No opinion on nil? but there are a couple examples of ?-methods
with blocks in core: any?, all?.

Gary W.


#6

On 3/21/07, Gary W. removed_email_address@domain.invalid wrote:

No opinion on nil? but there are a couple examples of ?-methods
with blocks in core: any?, all?.

Agreed. However, the methods you mention use the block in order
determine whether the predicate is satisfied or not. The action bit
is still just returning true or false.

The proposed ‘nil?’ uses the block to dispatch some unrelated
behavior, and it doesn’t return a boolean. I agree with David, et al.
on this one.


#7

Michiel De mare wrote:

If “nil?” is extended to accept a block, and to return self (for
Object) and the result of the yield (for NilClass), the following
notation becomes possible:

user.personal_data.address.nil?{ raise ‘no address!’}.zipcode

Why not use Object#tap?

user.p_data.address.tap {|a| raise “no address!” unless a}.zipcode

It’s included in Ruby 1.9, is easily definable otherwise
(http://rubyurl.com/eG5) and does basically what you want.

best,
Dan


#8

On Wed, Mar 21, 2007 at 09:55:07PM +0900, Michiel de Mare wrote:

end

If “nil?” is extended to accept a block, and to return self (for
Object) and the result of the yield (for NilClass), the following
notation becomes possible:

user.personal_data.address.nil?{ raise ‘no address!’}.zipcode

If your result set doesn’t include ‘false’, then you can use ‘or’. Mind
you,
it gets ugly with the necessary brackets:

(user.personal_data.address or raise “no address!”).zipcode

Especially if you go to multiple levels. It looks a bit too much like
LISP…

(((user or raise “no user!”).
personal_data or raise “no personal_data!”).
address or raise “no address!”).zipcode

Regards,

Brian.


#9

On 3/21/07, Michiel de Mare removed_email_address@domain.invalid wrote:

user.personal_data.address.nil?{ raise ‘no address!’}.zipcode

Instead of interrupting the main flow of code with error handling, how
about
this:

begin
zipcode = user.personal_data.address.zipcode
rescue NoMethodError => error
case error
when :personal_data then raise “No user!”
when :address then raise “No personal data!”
when :zipcode then raise “No address!”
end

In fact, thanks for spurring me to think of that technique, I think I’m
going to use it in future!