raganwald
(This is a snapshot of my old weblog. New posts and selected republished essays can be found at raganwald.com.)

Friday, February 01, 2008
  if_not_nil?


A fellow blogger emailed to ask what I thought of Object#if_not_nil. and how it compares to Object#andand, something I created for a similar purpose.

Well, technically they do exactly the same thing if you supply a block or fake it using something like Symbol#to_proc:

foo.if_not_nil { |f| f.bizzat } == foo.andand { |f| f.bizzat }

foo.if_not_nil(&:bizzat) == foo.andand(&:bizzat)

Object#andand does some proxy magic to let you invoke methods directly. At the moment,1 Object#if_not_nil does not. You can fake it with Symbol#to_proc if you don’t have parameters or a block, but if you want parameters or a block, you have to supply an explicit block to Object#if_not_nil:

foo.andand.bizzat == foo.if_not_nil(&:bizzat)

foo.andand.bizzat(:smyg, :tzzz) ==
foo.if_not_nil { |f| f.bizzat(:smyg, :tzzz) }

foo.andand.inject([]) { |a,n| a.me.push(n) } ==
foo.if_not_nil { |f|
f.inject([]) { |a,n| a.me.push(n) }
}

Do these extra explicit blocks matter? They matter to me, the whole point of the exercise was to make the code to do these things very easy to understand at a glance. Object#andand is an attempt to “decorate” a method call, not an attempt to call a particular block if and only if the parameter to the block is not null.

Now, I have read at least one view that Object#if_then_nil “expresses what the code is doing.” I think that is a Good Thing, code that says what it does. However, there is another view about code, a view that abstractions matter. Object#andand is not an attempt to provide an abbreviated way to write:

foo.bar if !foo.nil?

Instead, Object#andand attempts to create an entirely new kind of method call:

foo&&.bar

behind the scenes it may do exactly the same thing, or it may have a completely different implementation based on proxies. Object#andand presents a certain abstraction and does not ask you to think of proxies or blocks or if statements, just of a guarded method call.

Therefore, honestly, if you are already “thinking in if statements,” the existing Object#if_not_nil ought to be very natural: if statements delimit blocks of code. When what you are trying to emphasize with your code is the conditional and the block to be executed, the Object#if_not_nil is for you.

On the other hand, if you are thinking of a method invocation but want to guard it just in case the receiver is nil, then you may find yourself looking at Object#if_not_nil and asking yourself why you need a block and a parameter just to send something a method. When what you are trying to emphasize about the code is the method invocation, I suggest Object#andand is the more readable approach.

  1. Of course, Object#if_not_nil could gain this functionality in a future release. I think that would be very nice, but of course the author may not care for the style of coding or for the potential hassles of a method that returns a proxy object. Object#andand is released under the MIT license specifically to allow people to reuse all or part of it as they see appropriate.
 

Comments on “if_not_nil?:
Why not just use map and each?

zip = address.map { }
phone.each { }
 
Assaf:

Do you mean, why not use the method names #map and #each for the functionality of #andand?

Well, of course those methods don't work right now for arbitrary objects. But we could do so. If we did, how would we expect things to work for Enumerables?

For example, right now 5.andand * 2 => 10. And [1, 2, 3].andand * 2 => [1, 2, 3, 1, 2, 3].

If we used #map as for this functionality, I think you are proposing that 5.map { |n| n * 2 } => 10. But don't we want [1, 2, 3].map { |n| n * 2 } => [2, 4, 6]???
 
In my code I have cases that revolve around doing nothing with nil, applying once to scalar, and applying repeatedly for arrays. So map/each work just like you expect them to.

If I ever wanted to duplicate an array, I would:

Array(val) * 2

That would double-width an array when val is array or scalar, but return empty array when val is nil.

What I'm missing, if I had map/each that operate on object/nil, are the use cases for andand. Perhaps they just don't exist under the street lamp (my code base), but it's also possible I don't know how to find them.
 
Assaf:

if I had map/each that operate on object/nil, ...

So to re-iterate, are you saying that what you want is for map and each to operate on nil and on objects that are not Enumerables?

Or are you saying that in your code base you currently have a way to use map and each to send messages to objects that might be nil?
 




<< Home
Reg Braithwaite


Recent Writing
Homoiconic Technical Writing / raganwald.posterous.com

Books
What I‘ve Learned From Failure / Kestrels, Quirky Birds, and Hopeless Egocentricity

Share
rewrite_rails / andand / unfold.rb / string_to_proc.rb / dsl_and_let.rb / comprehension.rb / lazy_lists.rb

Beauty
IS-STRICTLY-EQUIVALENT-TO-A / Spaghetti-Western Coding / Golf is a good program spoiled / Programming conventions as signals / Not all functions should be object methods

The Not So Big Software Design / Writing programs for people to read / Why Why Functional Programming Matters Matters / But Y would I want to do a thing like this?

Work
The single most important thing you must do to improve your programming career / The Naïve Approach to Hiring People / No Disrespect / Take control of your interview / Three tips for getting a job through a recruiter / My favourite interview question

Management
Exception Handling in Software Development / What if powerful languages and idioms only work for small teams? / Bricks / Which theory fits the evidence? / Still failing, still learning / What I’ve learned from failure

Notation
The unary ampersand in Ruby / (1..100).inject(&:+) / The challenge of teaching yourself a programming language / The significance of the meta-circular interpreter / Block-Structured Javascript / Haskell, Ruby and Infinity / Closures and Higher-Order Functions

Opinion
Why Apple is more expensive than Amazon / Why we are the biggest obstacles to our own growth / Is software the documentation of business process mistakes? / We have lost control of the apparatus / What I’ve Learned From Sales I, II, III

Whimsey
The Narcissism of Small Code Differences / Billy Martin’s Technique for Managing his Manager / Three stories about The Tao / Programming Language Stories / Why You Need a Degree to Work For BigCo

History
06/04 / 07/04 / 08/04 / 09/04 / 10/04 / 11/04 / 12/04 / 01/05 / 02/05 / 03/05 / 04/05 / 06/05 / 07/05 / 08/05 / 09/05 / 10/05 / 11/05 / 01/06 / 02/06 / 03/06 / 04/06 / 05/06 / 06/06 / 07/06 / 08/06 / 09/06 / 10/06 / 11/06 / 12/06 / 01/07 / 02/07 / 03/07 / 04/07 / 05/07 / 06/07 / 07/07 / 08/07 / 09/07 / 10/07 / 11/07 / 12/07 / 01/08 / 02/08 / 03/08 / 04/08 / 05/08 / 06/08 / 07/08 /