avoiding nil.methodcalls short and cheap

T

Thorsten Rossner

Hi,

I often have to check if a methodcall A is not returning nil before
calling another method on the return value of the methodcall A. I'm
looking for a short and cheap (avoiding to call method A two times like
in this example where xmltag.get_text is method A:
textvalue = xmltag.get_text.value if xmltag.get_text
)
The ideal solution would be a oneliner with only little complexity
calling method A just one time. Am I using the wrong approach here
anyway?

Thanks,
tr!
 
A

akbarhome

Hi,

I often have to check if a methodcall A is not returning nil before
calling another method on the return value of the methodcall A. I'm
looking for a short and cheap (avoiding to call method A two times like
in this example where xmltag.get_text is method A:
textvalue = xmltag.get_text.value if xmltag.get_text
)
The ideal solution would be a oneliner with only little complexity
calling method A just one time. Am I using the wrong approach here
anyway?

Thanks,
tr!
Consider this:

begin
textvalue = xmltag.get_text.value
rescue NoMethodError
end
 
E

Eivind Eklund

Hi,

I often have to check if a methodcall A is not returning nil before
calling another method on the return value of the methodcall A. I'm
looking for a short and cheap (avoiding to call method A two times like
in this example where xmltag.get_text is method A:
textvalue = xmltag.get_text.value if xmltag.get_text

Variations using just logic operators and conditionals - I think the
last two are probably best (though it vary by context)

# Short, unclear
textvalue = xmltag.get_text
textvalue &&= textvalue.value

# With if
textvalue = textvalue.value if textvalue = xmltag.get_text

# Longer, clearer
if textvalue = xmltag.get_text
textvalue = textvalue.value
end

# Re-using the variable, one-line
textvalue = xmltag.get_text and textvalue = textvalue.value

# With new variable, possibly the cleanest
textvalue = (text = xmltag.get_text and text.value)

# You can also redefine the object in question
def xmltag.get_text_value
text = get_text && text.value
end
textvalue = xmltag.get_text_value
 
M

Michal Suchanek

Variations using just logic operators and conditionals - I think the
last two are probably best (though it vary by context)

# Short, unclear
textvalue = xmltag.get_text
textvalue &&= textvalue.value

# With if
textvalue = textvalue.value if textvalue = xmltag.get_text

# Longer, clearer
if textvalue = xmltag.get_text
textvalue = textvalue.value
end

# Re-using the variable, one-line
textvalue = xmltag.get_text and textvalue = textvalue.value

# With new variable, possibly the cleanest
textvalue = (text = xmltag.get_text and text.value)

# You can also redefine the object in question
def xmltag.get_text_value
text = get_text && text.value
end
textvalue = xmltag.get_text_value

I would avoid code that relies on logical operators to preserve values.
That is any code that uses logical operators and would break if you
put !!() around the expression is quite dodgy in my book.

Thanks

Michal
 
E

Eivind Eklund

I would avoid code that relies on logical operators to preserve values.
That is any code that uses logical operators and would break if you
put !!() around the expression is quite dodgy in my book.

And I would strongly disagree with this.

Returning the value of the true expression is defined behavior, and it
allows for considerable simplification of code. Having simpler code
means that you have less chance of errors and spend less time when
reading the code.

To my mind, dodgy code is code that is difficult to understand or
increase chance of errors, ie, the opposite of code that does
appropriate use of the preservation of values in logical operators.

Not that I'm particularly happy with any of the cases above - the
closest to "good" I found was textvalue = (text = tag.get_text and
text.get_value)

Eivind.
 
T

Trans

Hi,

I often have to check if a methodcall A is not returning nil before
calling another method on the return value of the methodcall A. I'm
looking for a short and cheap (avoiding to call method A two times like
in this example where xmltag.get_text is method A:
textvalue = xmltag.get_text.value if xmltag.get_text
)
The ideal solution would be a oneliner with only little complexity
calling method A just one time. Am I using the wrong approach here
anyway?

The conceptually simplest is:

get_text = xmltag.get_text
textvalue = get_text.value if get_text

But as you will note, it takes up two lines. Of course you can always
fix that:

get_text = xmltag.get_text; textvalue = get_text.value if get_text

Even so, I often write this as:

if get_text = xmltag.get_text
textvalue = get_text.value
end

Simply b/c I often find long one liners unpleasant to read.

However, I just want to point out that if there is no get_text, than
textvalue is not being assigned at all. Unless you have previously set
textvalue to a default, that could lead to an undefined local variable
error. So you might consider:

textvalue = (get_text = xmltag.get_text) ? get_text.value : nil

T.
 
T

Thorsten Rossner

Thanks for quick and helpful answers! I hope all of you will have a
great weekend!
 
D

Daniel Martin

Thorsten Rossner said:
The ideal solution would be a oneliner with only little complexity
calling method A just one time. Am I using the wrong approach here
anyway?

Since no one has pointed this out, I'll remind you that this *is*
Ruby, which has the relatively uncommon feature of allowing one to add
to core classes, so we can use that if there's only one or two methods
you'd like to call frequently. First note:

irb(main):001:0> nil.freeze
=> nil

Then, doing this somewhere:

class NilClass
alias value freeze
end

Lets us do this in the main code:

textvalue = xmltag.get_text.value

And nils automatically get propagated.
 
D

Daniel DeLorme

Thorsten said:
Hi,

I often have to check if a methodcall A is not returning nil before
calling another method on the return value of the methodcall A. I'm
looking for a short and cheap (avoiding to call method A two times like
in this example where xmltag.get_text is method A:
textvalue = xmltag.get_text.value if xmltag.get_text
)
The ideal solution would be a oneliner with only little complexity
calling method A just one time. Am I using the wrong approach here
anyway?

This the solution I use in *my* code for this common problem:
textvalue = xmltag.get_text.ergo.value

This is my personal solution and I've never seen anyone use something
like this, so it may not be a "common and accepted idiom" but it works
well for me. YMMV

code for "ergo":

class NilClass
def ergo
@blackhole ||= Object.new.instance_eval do
class << self
for m in public_instance_methods
undef_method(m.to_sym) unless m =~ /^__.*__$/
end
end
def method_missing(*args); nil; end
self
end
@blackhole unless block_given?
end
end

class Object
def ergo
if block_given?
yield(self)
else
self
end
end
end
 
T

Trans

This the solution I use in *my* code for this common problem:
textvalue = xmltag.get_text.ergo.value

This is my personal solution and I've never seen anyone use something
like this, so it may not be a "common and accepted idiom" but it works
well for me. YMMV

code for "ergo":

class NilClass
def ergo
@blackhole ||= Object.new.instance_eval do
class << self
for m in public_instance_methods
undef_method(m.to_sym) unless m =~ /^__.*__$/
end
end
def method_missing(*args); nil; end
self
end
@blackhole unless block_given?
end
end

class Object
def ergo
if block_given?
yield(self)
else
self
end
end
end

Interesting... I like this. Nice use of fluent / magic dot notation.
This is a good use case for Functor too (see Facets):

class NilClass
def ergo
@blackhole ||= Functor.new{ nil }
@blackhole unless block_given?
end
end

One question, I'm not sure what you expect to be returned if a block
is given.

Besides that one issue, I'd like to add this to Facets and credit you.

T.
 
D

Daniel DeLorme

Trans said:
Interesting... I like this. Nice use of fluent / magic dot notation.
This is a good use case for Functor too (see Facets):

class NilClass
def ergo
@blackhole ||= Functor.new{ nil }
@blackhole unless block_given?
end
end

Indeed, that really simplifies the code.
One question, I'm not sure what you expect to be returned if a block
is given.

foo.ergo{ |o| o.bar } #=> result of bar
nil.ergo{ |o| o.bar } #=> nil
because sometimes some cases don't lend themselves very well to "magic
dot" notation.
Besides that one issue, I'd like to add this to Facets and credit you.

By all means, I'd be honored.

Daniel
 
E

Eivind Eklund

This the solution I use in *my* code for this common problem:
textvalue = xmltag.get_text.ergo.value

This is my personal solution and I've never seen anyone use something
like this, so it may not be a "common and accepted idiom" but it works
well for me. YMMV

code for "ergo":

(... deleted ...)

I love the solution, and have one question: Is there any deeper
rationale behind the name "ergo"?

Eivind.
 
M

Michal Suchanek

And I would strongly disagree with this.

Returning the value of the true expression is defined behavior, and it
allows for considerable simplification of code. Having simpler code
means that you have less chance of errors and spend less time when
reading the code.
Not always. It is defined behavior but unless you work exclusively in
Ruby you will have to deal with cases when true values are not
preserved. This makes the code more obscure, harder to read, and more
error prone.

The concept of logical operations is to preserve the logical value,
and you have to think twice about other properties they might or might
not have.

Thanks

Michal
 
D

Daniel DeLorme

Eivind said:
I love the solution, and have one question: Is there any deeper
rationale behind the name "ergo"?

it's latin for "therefore", as in:

cogito = [2,2]
cogito.ergo.sum #=> 4

;-)
 
E

Eivind Eklund

it's latin for "therefore", as in:

I know that (ergo is commonly used in my native tongue), I just didn't
feel that it really formed a rationale. On the other hand, I don't
have a better name handy...

Eivind.
 
D

Daniel DeLorme

Eivind said:
I know that (ergo is commonly used in my native tongue), I just didn't
feel that it really formed a rationale. On the other hand, I don't
have a better name handy...

Sorry, it seems I misunderstood the question. But I don't know what more
'rationale' is needed. To me, "a ergo b" reads like "b proceeds from a",
which expresses the same logic as "a && a.b" IMHO no deeper rationale
is required.

Daniel
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,262
Messages
2,571,310
Members
47,977
Latest member
MillaDowdy

Latest Threads

Top