Emulating a Groovy feature?

J

John Wells

Guys,

There's something called Safe Navigation in Groovy (http://groovy.codehaus.org/Statements#Statements-Safenavigation) that I find very appealing.

So, Groovy will allow you to safely walk an object graph, even if that graph has nulls in it (the walk will short circuit):

def foo = null
def bar = foo?.something?.myMethod()
assert bar == null

Is there an easy way to hack an equivalent out in Ruby?

Thanks,
John
 
X

Xavier Noria

Guys,

There's something called Safe Navigation in Groovy (http://
groovy.codehaus.org/Statements#Statements-Safenavigation) that I
find very appealing.

So, Groovy will allow you to safely walk an object graph, even if
that graph has nulls in it (the walk will short circuit):

def foo = null
def bar = foo?.something?.myMethod()
assert bar == null

Is there an easy way to hack an equivalent out in Ruby?

Sure, NilClass can be reopened just fine:

class NilClass
def method_missing(name, *args)
nil
end
end

nil.invoices.find(5) # -> nil

Reopening NilClass is something really handy sometimes.

-- fxn
 
D

Dave Burt

John said:
Guys,

There's something called Safe Navigation in Groovy (http://groovy.codehaus.org/Statements#Statements-Safenavigation) that I find very appealing.

So, Groovy will allow you to safely walk an object graph, even if that graph has nulls in it (the walk will short circuit):

def foo = null
def bar = foo?.something?.myMethod()
assert bar == null

Is there an easy way to hack an equivalent out in Ruby?

irb(main):001:0> def nil.method_missing(*_) end
=> nil
irb(main):002:0> nil.a.b.c
=> nil

Cheers,
Dave
 
X

Xavier Noria

The only problem I can see here is that it's not quite optional.

Yeah, that's the basic technique.

You could use some naming convention as in Groovy as well (which you
would check in the body of method_missing), or perhaps wrap the
activation of that catchall in a block like this:

with_safe_navigation do
regular code
end

Once you can reopen NilClass like that I think there are ways to
emulate that feature.

-- fxn
 
S

Stefan Rusterholz

John said:
Guys,

There's something called Safe Navigation in Groovy
(http://groovy.codehaus.org/Statements#Statements-Safenavigation) that I
find very appealing.

So, Groovy will allow you to safely walk an object graph, even if that
graph has nulls in it (the walk will short circuit):

def foo = null
def bar = foo?.something?.myMethod()
assert bar == null

Is there an easy way to hack an equivalent out in Ruby?

Thanks,
John

Good to see that I'm not alone on this :)
I still intend to write an RCR for this. You might want to read through
http://www.ruby-forum.com/topic/115720

Regards
Stefan
 
R

Robert Klemme

2007/8/18 said:
Guys,

There's something called Safe Navigation in Groovy (http://groovy.codehaus.org/Statements#Statements-Safenavigation) that I find very appealing.

So, Groovy will allow you to safely walk an object graph, even if that graph has nulls in it (the walk will short circuit):

def foo = null
def bar = foo?.something?.myMethod()
assert bar == null

Is there an easy way to hack an equivalent out in Ruby?

Try this in IRB

class Object
class Holder
def __ref() @ref end

def initialize(ref,&b)
@ref = ref
instance_eval(&b)
end

def method_missing(s,*a,&b)
@ref = @ref.send(s,*a,&b) rescue nil
self
end
end

def safe(&b)
Holder.new(self,&b).__ref
end
end
F = Struct.new :name
f=F.new "foo"
f.safe { name.size > 10 }
f.safe { name.xxx }

Not too nice though...

Kind regards

robert
 
R

Robert Klemme

2007/8/20 said:
Try this in IRB

class Object
class Holder
def __ref() @ref end

def initialize(ref,&b)
@ref = ref
instance_eval(&b)
end

def method_missing(s,*a,&b)
@ref = @ref.send(s,*a,&b) rescue nil
self
end
end

def safe(&b)
Holder.new(self,&b).__ref
end
end
F = Struct.new :name
f=F.new "foo"
f.safe { name.size > 10 }
f.safe { name.xxx }

Not too nice though...

Kind regards

robert

PS: Forgot to mention: you can always use the "rescue" modifier:

x = foo.bar.baz rescue nil
 
J

Jake Cutter

x = foo.bar.baz rescue nil

Interesting thread....glad someone else has hit this need.

How does the above work, exactly? You're rescuing without a block?
Doesn't it have to be something like:

begin
x = foo.bar.baz
rescue nil
end

? At least the documentation would suggest so...

Thanks,
Jake
 
J

Jason Roelofs

Note: parts of this message were removed by the gateway to make it a legal Usenet post.

Interesting thread....glad someone else has hit this need.

How does the above work, exactly? You're rescuing without a block?
Doesn't it have to be something like:

begin
x = foo.bar.baz
rescue nil
end

? At least the documentation would suggest so...

Thanks,
Jake
You don't need begin unless you want to catch just a section of code. Single
lines works wonders for concise error handling, such as

this_method_can_throw rescue "default value"

Or for a full method (Rails)

def action
does_stuff_that_can_throw
rescue
default_error_handling
end

Ruby's awesome.

Jason
 
R

Robert Klemme

Interesting thread....glad someone else has hit this need.

How does the above work, exactly? You're rescuing without a block?
Doesn't it have to be something like:

begin
x = foo.bar.baz
rescue nil
end

? At least the documentation would suggest so...

The value of the expression is replaced by the value of the expression
after the "rescue" in this form. And no, you don't need a block. The
Pickaxe 1st edition does not seem to mention it but it's definitively a
legal Ruby feature.

Kind regards

robert
 
T

thefed

The value of the expression is replaced by the value of the
expression after the "rescue" in this form. And no, you don't need
a block. The Pickaxe 1st edition does not seem to mention it but
it's definitively a legal Ruby feature.

Begin-Rescue-End blocks tend to slow things down significantly - does
JUST including rescue also take those penalties to speed?

- Ari Brown
 
L

Luke Melia

Begin-Rescue-End blocks tend to slow things down significantly -
does JUST including rescue also take those penalties to speed?

Sebastian Delmont did some benchmarking on this last year.

http://www.notsostupid.com/blog/2006/08/31/the-price-of-a-rescue/

His conclusions: "Using rescue is not much more expensive than running
naked. In my tests in particular, it never was more than 5% slower. It
might even be cheaper than multiple tests."

Cheers from NYC,
Luke
 
C

Charles Oliver Nutter

Luke said:
Sebastian Delmont did some benchmarking on this last year.

http://www.notsostupid.com/blog/2006/08/31/the-price-of-a-rescue/

His conclusions: "Using rescue is not much more expensive than running
naked. In my tests in particular, it never was more than 5% slower. It
might even be cheaper than multiple tests."

It slows down JRuby a bit more than Ruby 1.8, unfortunately, because of
the way the JVM manages exception handling and operand stacks. But
generally you won't notice it, since nontrivial code bodies will be
quite a bit faster than 1.8.

~/NetBeansProjects/jruby-current $ jruby -J-server test.rb
user system total real
plain 0.493000 0.000000 0.493000 ( 0.493000)
plain 0.474000 0.000000 0.474000 ( 0.474000)
plain 0.222000 0.000000 0.222000 ( 0.222000)
plain 0.220000 0.000000 0.220000 ( 0.220000)
plain 0.219000 0.000000 0.219000 ( 0.219000)
safe 0.431000 0.000000 0.431000 ( 0.431000)
safe 0.338000 0.000000 0.338000 ( 0.337000)
safe 0.320000 0.000000 0.320000 ( 0.320000)
safe 0.317000 0.000000 0.317000 ( 0.316000)
safe 0.319000 0.000000 0.319000 ( 0.319000)
rescue 0.315000 0.000000 0.315000 ( 0.315000)
rescue 0.313000 0.000000 0.313000 ( 0.313000)
rescue 0.314000 0.000000 0.314000 ( 0.314000)
rescue 0.312000 0.000000 0.312000 ( 0.312000)
rescue 0.312000 0.000000 0.312000 ( 0.313000)
~/NetBeansProjects/jruby-current $ ruby test.rb
user system total real
plain 0.210000 0.000000 0.210000 ( 0.214133)
plain 0.210000 0.000000 0.210000 ( 0.228721)
plain 0.200000 0.000000 0.200000 ( 0.206997)
plain 0.210000 0.000000 0.210000 ( 0.206290)
plain 0.200000 0.010000 0.210000 ( 0.205982)
safe 0.240000 0.000000 0.240000 ( 0.238373)
safe 0.230000 0.000000 0.230000 ( 0.243327)
safe 0.240000 0.000000 0.240000 ( 0.237573)
safe 0.230000 0.000000 0.230000 ( 0.242121)
safe 0.240000 0.000000 0.240000 ( 0.237385)
rescue 0.230000 0.000000 0.230000 ( 0.238207)
rescue 0.230000 0.000000 0.230000 ( 0.235387)
rescue 0.230000 0.000000 0.230000 ( 0.234333)
rescue 0.240000 0.000000 0.240000 ( 0.234938)
rescue 0.230000 0.000000 0.230000 ( 0.240186)

- Charlie
 
C

Charles Oliver Nutter

Charles said:
It slows down JRuby a bit more than Ruby 1.8, unfortunately, because of
the way the JVM manages exception handling and operand stacks. But
generally you won't notice it, since nontrivial code bodies will be
quite a bit faster than 1.8.

Actually, it just occurred to me that this is entirely a factor of local
variable handling; when rescue is involved, JRuby has to switch from
stack-based local variables to heap-based local vars. I'm working on a
few ways to mitigate this cost for the general case, so this perf hit
may go away.

- Charlie
 
R

Robert Dober

Actually, it just occurred to me that this is entirely a factor of local
variable handling; when rescue is involved, JRuby has to switch from
stack-based local variables to heap-based local vars. I'm working on a
few ways to mitigate this cost for the general case, so this perf hit
may go away.

- Charlie
It seems however that the rescue clause is not part of Ruby1.9
anymore, maybe, if this is confirmed, you might not want to optimize
this.

Cheers
Robert
 
J

John Wells

It seems however that the rescue clause is not part of Ruby1.9
anymore, maybe, if this is confirmed, you might not want to optimize
this.

You mean the one line rescue syntax mentioned before? Interesting, I
can't find any docs via Google on this being removed in 1.9. Where are
you finding this info?

Seems strange that such a useful feature would be removed...
 
R

Robert Dober

You mean the one line rescue syntax mentioned before? Interesting, I
can't find any docs via Google on this being removed in 1.9. Where are
you finding this info?
From Ruby 1.9 itself ;)
I thought, :(

But I just cross checked and it works like charm.

Very sorry about the noise.


Cheers
Robert
 

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,270
Messages
2,571,353
Members
48,038
Latest member
HunterDela

Latest Threads

Top