RCR? Added syntax for chains that possibly return nil

S

Stefan Rusterholz

One type of construct in ruby that somewhat annoys me and IMHO reduces
readability is working around cases where an intermediate method might
return nil. Those cases are e.g.

while (line = gets && line.chomp)
foo = (bar && bar.baz && bar.baz.quuz) || default # bar might be nil, so
might bar.baz and bar.baz.quuz

There are some more constructs, most of you surely already used such
stuff or just spread it onto a bit more of lines.
While all of this is fine, maybe that could be improved. My RCR would be
to introduce a new syntax besides . for method invocation, which on the
first nil aborts any further method calls on it and returns nil.

E.g. the above examples would become:
while line = gets->chomp
foo = bar->baz->quuz || default

-> was the first best that came to my mind, if you have better ideas,
please state.
Unlike e.g. making nil to respond to every method returning self, this
solution would put the coder in power as to where he cares about getting
nil (e.g. he wants an exception if foo is nil, so he just writes
foo.bar, if he doesn't want one, he writes foo->bar) and where he just
wants to get the nil value back.

Do you think this RCR is worth to make?

Regards
Stefan
 
E

Eugen Minciu

Excerpts from Stefan Rusterholz's message of Thu Jul 19 16:31:01 +0300 2007:
One type of construct in ruby that somewhat annoys me and IMHO reduces
readability is working around cases where an intermediate method might
return nil. Those cases are e.g.

while (line = gets && line.chomp)
foo = (bar && bar.baz && bar.baz.quuz) || default # bar might be nil, so
might bar.baz and bar.baz.quuz

There are some more constructs, most of you surely already used such
stuff or just spread it onto a bit more of lines.
While all of this is fine, maybe that could be improved. My RCR would be
to introduce a new syntax besides . for method invocation, which on the
first nil aborts any further method calls on it and returns nil.

E.g. the above examples would become:
while line = gets->chomp
foo = bar->baz->quuz || default

-> was the first best that came to my mind, if you have better ideas,
please state.
Unlike e.g. making nil to respond to every method returning self, this
solution would put the coder in power as to where he cares about getting
nil (e.g. he wants an exception if foo is nil, so he just writes
foo.bar, if he doesn't want one, he writes foo->bar) and where he just
wants to get the nil value back.

Do you think this RCR is worth to make?

Regards
Stefan
how about this ugly creature?

<hack>
class Object
alias regular_method_missing method_missing
def method_missing(meth)
if meth.to_s[0]=='_'[0]
return nil
else
regular_method_missing(meth)
end
end
end

puts "yay, it works" unless 3._foo._bar._baz
</hack>

Not exactly like your syntax but gets the job done.

Also, you might want to consider the proper way(tm) of doing it (I think)

<proper way>

begin
foo.bar.baz.whatever
rescue
puts "yay it works"
end

</proper way>

Also works as a one liner:
x = begin 3.foo.bar.baz; rescue; nil; end
 
S

Stefan Rusterholz

Eugen said:
Excerpts from Stefan Rusterholz's message of Thu Jul 19 16:31:01 +0300
2007:
While all of this is fine, maybe that could be improved. My RCR would be
solution would put the coder in power as to where he cares about getting
nil (e.g. he wants an exception if foo is nil, so he just writes
foo.bar, if he doesn't want one, he writes foo->bar) and where he just
wants to get the nil value back.

Do you think this RCR is worth to make?

Regards
Stefan
how about this ugly creature?

<hack>
class Object
alias regular_method_missing method_missing
def method_missing(meth)
if meth.to_s[0]=='_'[0]
return nil
else
regular_method_missing(meth)
end
end
end

Interesting idea :) This one won't work right away I think, but it's a
starting point.
Btw., you can do ?_ instead of '_'[0]. Syntax would have the benefit of
optimization, though.
<proper way>
begin
foo.bar.baz.whatever
rescue
puts "yay it works"
end

You'd have to rescue NoMethodError, since else you could miss important
exceptions. For the same reason, single line rescue is disqualified. And
with a rescue, it is even less readable than working around the nil case
via multiple lines :-/

Regards
Stefan
 
R

Robert Dober

One type of construct in ruby that somewhat annoys me and IMHO reduces
readability is working around cases where an intermediate method might
return nil. Those cases are e.g.

while (line = gets && line.chomp)
foo = (bar && bar.baz && bar.baz.quuz) || default # bar might be nil, so
might bar.baz and bar.baz.quuz

There are some more constructs, most of you surely already used such
stuff or just spread it onto a bit more of lines.
While all of this is fine, maybe that could be improved. My RCR would be
to introduce a new syntax besides . for method invocation, which on the
first nil aborts any further method calls on it and returns nil.

E.g. the above examples would become:
while line = gets->chomp
foo = bar->baz->quuz || default

-> was the first best that came to my mind, if you have better ideas,
please state.
Unlike e.g. making nil to respond to every method returning self, this
solution would put the coder in power as to where he cares about getting
nil (e.g. he wants an exception if foo is nil, so he just writes
foo.bar, if he doesn't want one, he writes foo->bar) and where he just
wants to get the nil value back.

Do you think this RCR is worth to make?

Regards
Stefan
Hmm I thought we have had this already, but I do not think somebody
made such a concise proposal yet; I am slightly in favor, but gotta
think a lot about it before giving a definite vote, just wanted to
give you some positive feed back for now...

Robert
 
S

Stefan Rusterholz

Eugen said:
<hack>
class Object
alias regular_method_missing method_missing
def method_missing(meth)
if meth.to_s[0]=='_'[0]
return nil
else
regular_method_missing(meth)
end
end
end

Reworked it a bit:
class Object
alias method_missing_no_underscore method_missing
def method_missing(m,*a,&b)
m.to_s[0] == ?_ ? send(m.to_s[1..-1],*a,&b) :
method_missing_no_underscore(m,*a,&b)
end
end

class NilClass
alias method_missing_no_underscore method_missing
def method_missing(m,*a,&b)
m.to_s[0] == ?_ ? nil : method_missing_no_underscore(m,*a,&b)
end
end

while line = gets._chomp
...
end

so ._ simulates -> with that hack. Thanks for the idea. Should check for
m.to_s != "_", though, since that method is used e.g. with gettext.

Regards
Stefan
 
G

gabriele renzi

Do you think this RCR is worth to make?

Sorry, I may have missed this in the previous thread, but why an inline
rescue clause is not enough?

i.e.

while (line = gets && line.chomp)
foo = (bar && bar.baz && bar.baz.quuz) || default # bar might be nil, so
might bar.baz and bar.baz.quuz

could be

while line = gets.chomp rescue nil
foo = bar.baz.quuz rescue default
end

I admit I have a strange feeling about a raising loop condition.. but
something like the second line I wrote many times.

This is slightly longer and gives you a little bit less control,
but I think that complex one-line flow control may be a bad thing.

Just my two cents, obviously.
 
S

Stefan Rusterholz

gabriele said:
Sorry, I may have missed this in the previous thread, but why an inline
rescue clause is not enough?
while line = gets.chomp rescue nil
foo = bar.baz.quuz rescue default
end

Simple: what if your exception is not the NoMethodError you are worried
about? You'll never know.
Or less dramatic: a bug may stay in your app unnoticed for longer than
necessary.

Regards
Stefan
 
R

Robert Dober

Eugen said:
<hack>
class Object
alias regular_method_missing method_missing
def method_missing(meth)
if meth.to_s[0]=='_'[0]
return nil
else
regular_method_missing(meth)
end
end
end

Reworked it a bit:
class Object
alias method_missing_no_underscore method_missing
def method_missing(m,*a,&b)
m.to_s[0] == ?_ ? send(m.to_s[1..-1],*a,&b) :
method_missing_no_underscore(m,*a,&b)
end
end

class NilClass
alias method_missing_no_underscore method_missing
def method_missing(m,*a,&b)
m.to_s[0] == ?_ ? nil : method_missing_no_underscore(m,*a,&b)
end
end

while line = gets._chomp
...
end

so ._ simulates -> with that hack. Thanks for the idea. Should check for
m.to_s != "_", though, since that method is used e.g. with gettext.

Regards
Stefan

I hate it, this really should be syntax and furthermore it would make
code ugly ( I know beauty lies in the eyes of the beholder ;).
The original idea is quite good, why your shift?

This is a terrible name pollution amongst other bad things :(

Robert
 
S

Stefan Rusterholz

Robert Dober wrote:
I hate it, this really should be syntax and furthermore it would make
code ugly ( I know beauty lies in the eyes of the beholder ;).
The original idea is quite good, why your shift?

This is a terrible name pollution amongst other bad things :(

Robert

Keyword is "simulate" ;-)
This can help trying it out. And I fully agree, that hack is ugly, also
I wouldn't use it in actual code simply because it is "magic" (you have
to know that _* methods are magic, that's not good for code to be
maintained).

Regards
Stefan
 
T

Trans

One type of construct in ruby that somewhat annoys me and IMHO reduces
readability is working around cases where an intermediate method might
return nil. Those cases are e.g.

while (line = gets && line.chomp)
foo = (bar && bar.baz && bar.baz.quuz) || default # bar might be nil, so
might bar.baz and bar.baz.quuz

what about

foo = bar.baz.quuz rescue default

T.
 
S

Stefan Rusterholz

Trans said:
what about
foo = bar.baz.quuz rescue default

See the answer to gabriele renzi (Simple: what if your exception is not
the NoMethodError you are worried
about? You'll never know.)

Regards
Stefan
 
T

Trans

See the answer to gabriele renzi (Simple: what if your exception is not
the NoMethodError you are worried
about? You'll never know.)

I think it rescues all StandardErrors.

T.
 
S

Stefan Rusterholz

Trans said:
I think it rescues all StandardErrors.

T.

Um, Yes, that's exactly the problem about it, wasn't that clear? It
rescues *all* StandardErrors. So you won't notice if something else than
your NoMethodError - which nil would raise - has happened.
Say your gets raises a BrokenPipe exception or similar since the server
you were connected disconnects you because of bad input you send. Your
will never see an exception being raised since that happens in the gets
you just rescue all standarderrors from.

Regards
Stefan
 
E

Eugen Minciu

Excerpts from Stefan Rusterholz's message of Fri Jul 20 01:06:13 +0300 2007:
Robert Dober wrote:



Keyword is "simulate" ;-)
This can help trying it out. And I fully agree, that hack is ugly, also
I wouldn't use it in actual code simply because it is "magic" (you have
to know that _* methods are magic, that's not good for code to be
maintained).

Regards
Stefan
Hi, again
I've been thinking about this a bit more. Here's another approach you
could try. This actually feels a lot less like a hack.

Advantages:
- no namespace pollution
- clarifies what you're trying to do

Disadvantage:
- every time you call it you add an extra, intermediary call to, well,
call() :)

However, I'm sure that I'm getting something wrong and, again, you can
simplify this to some extent, and improve it. It's just the idea itself
that I wanted to show you. I'm certain you'll find a way to ditch the
call()

def nilsafe(&blk)
x=Proc.new { Object.method_missing }
def method_missing(meth,*args,&blk)
return nil
end

begin
yield blk
rescue => e
def method_missing(meth,*args,&blk)
x.call(meth,args,&blk)
end
raise e
end

def method_missing(meth,*args,&blk)
x.call(meth,args,&blk)
end
end

So from an IRB prompt, after loading it, you can do:

irb(main):002:0> 3.foo.bar.bz
NoMethodError: undefined method `foo' for 3:Fixnum
from (irb):2
irb(main):003:0> nilsafe { 3.foo.bar.baz }
=> nil
irb(main):004:0> nilsafe do
irb(main):005:1* 3.foo.bar.baz
irb(main):006:1> raise "Some Error here"
irb(main):007:1> end
RuntimeError: Some Error here
from (irb):6
from ./safeattr2.rb:8:in `nilsafe'
from (irb):4
from :0

the raise could be changed a little to output some nicer results (using caller) and other such niceties. But it is late now ... ;)

Let me know what you think of this one
Cheers,
 
T

Trans

Um, Yes, that's exactly the problem about it, wasn't that clear? It
rescues *all* StandardErrors. So you won't notice if something else than
your NoMethodError - which nil would raise - has happened.
Say your gets raises a BrokenPipe exception or similar since the server
you were connected disconnects you because of bad input you send. Your
will never see an exception being raised since that happens in the gets
you just rescue all standarderrors from.

So a better RCR maybe it just to qualify the line rescue.
NoMethodError being the more common, maybe a special rescue for that.
Eg.

bar.baz.quuz ||| default

T.
 
N

Nobuyoshi Nakada

R

Robert Dober

So a better RCR maybe it just to qualify the line rescue.
NoMethodError being the more common, maybe a special rescue for that.
Eg.

bar.baz.quuz ||| default
Hmm I like the expressiveness of the original proposal
look at this example

bar->baz.foo, which has to be written as
(bar.baz ||| default).foo

of course one could write
bar.baz.foo ||| default
but it has more permissive semantics and will hide errors that
bar->baz.foo
will raise
Robert
 
T

Trans

Hmm I like the expressiveness of the original proposal
look at this example

bar->baz.foo, which has to be written as
(bar.baz ||| default).foo

of course one could write
bar.baz.foo ||| default
but it has more permissive semantics and will hide errors that
bar->baz.foo
will raise
Robert

But where is default in your example? You'd have:

(bar->baz || default).foo

T.
 

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
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top