One Liner Problem / MaybeMonad


#1

Stump the list challenge for a Friday. The following
code has a reoccuring pattern. The chain of calls
can’t continue if a nil is returned. So what would be
a one-liner is now broken into three. This occurs in
several places in the Ruby code. What would be good is
if this could be turned into a clean one-liner. No
fancy gobbly-gook strung out over 80 characters…

def self.locate( path )
d = TupleDomain.locate( path )
return nil unless d
d.tuple
end

Things like the following are not desired, because
they make ugly looking chains.
def self.locate( path )
(d = self.locate( path ) ? nil : d.tuple)
end

HINT: What would be better would be something like
this:

class self.locate( path )
TupleDomain.locate_domain( path
).return_on_nil.tuple
end

Hmmmm… How to do that? Add a method to Object
maybe?


SPOILERS BELOW
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

At first I thought just add a routine to Object
return_on_nil, and for the nil object just have it
define a method_missing that just returns nil. So that
all nils pass through unharmed.

Bzzzzt. That didn’t work. nil is a singleton. I mean a
real solid singleton, defined in the core of the
language. No easy reflection is going to change that,
and gosh how much it would probably screw up.

So then I decided to create a mirror of nil, that does
the chaining. This is what I came up with:

class ChainableNil
NilClass.methods.reject {|m| m =~
/(send|eval|id)/}.each do |m|
a=NilClass.method(m.to_sym).arity
case
when a == 0
eval “def #{m}() nil.send(:#{m}); end”
when a > 0
eval “def #{m}(*args) nil.send(:#{m}, args);
end”
else
eval “def #{m}(*args,&block) nil.send(:#{m},
args, block); end”
end
end
def method_missing(symbol, *args) self end
end

class Object
def return_on_nil() (self.nil? ? ChainableNil.new :
self) end
end

Test code, should print an even mixture of false and

trues.
def foo() (rand(10) > 4 ? nil : ‘test’) end
20.times {puts foo.return_on_nil.length.nil?}

Now it would be even better that if once invoke in a
chain any routine that fails nils the chain-- like a
true MaybeMonad.
http://moonbase.rydia.net/mental/writings/programming/monads-in-ruby/

The true monad solution however invokes a lot of code
and turns this into a non one-liner stringable chain
of routines.

So anyone up for the second challenge? I have doubts
it can be done.

Shawn


Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com