RCR? Added syntax for chains that possibly return nil

Discussion in 'Ruby' started by Stefan Rusterholz, Jul 19, 2007.

  1. 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

    --
    Posted via http://www.ruby-forum.com/.
     
    Stefan Rusterholz, Jul 19, 2007
    #1
    1. Advertising

  2. Stefan Rusterholz

    Eugen Minciu Guest

    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

    --
    Eugen Minciu.

    Wasting valuable time since 1985.
     
    Eugen Minciu, Jul 19, 2007
    #2
    1. Advertising

  3. Eugen Minciu wrote:
    > 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

    --
    Posted via http://www.ruby-forum.com/.
     
    Stefan Rusterholz, Jul 19, 2007
    #3
  4. Stefan Rusterholz

    Robert Dober Guest

    On 7/19/07, Stefan Rusterholz <> wrote:
    > 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
    >
    > --
    > Posted via http://www.ruby-forum.com/.
    >
    >

    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


    --
    I always knew that one day Smalltalk would replace Java.
    I just didn't know it would be called Ruby
    -- Kent Beck
     
    Robert Dober, Jul 19, 2007
    #4
  5. Eugen Minciu wrote:
    > <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

    --
    Posted via http://www.ruby-forum.com/.
     
    Stefan Rusterholz, Jul 19, 2007
    #5
  6. On Thu, 19 Jul 2007 22:31:01 +0900, Stefan Rusterholz wrote:


    > 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.



    --
    goto 10: http://www.goto10.it
    blog it: http://riffraff.blogsome.com
    blog en: http://www.riffraff.info
     
    gabriele renzi, Jul 19, 2007
    #6
  7. gabriele renzi wrote:
    > On Thu, 19 Jul 2007 22:31:01 +0900, Stefan Rusterholz wrote:
    >
    >
    >> 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?


    > 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

    --
    Posted via http://www.ruby-forum.com/.
     
    Stefan Rusterholz, Jul 19, 2007
    #7
  8. Stefan Rusterholz

    Robert Dober Guest

    On 7/19/07, Stefan Rusterholz <> wrote:
    > Eugen Minciu wrote:
    > > <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
    >
    > --
    > Posted via http://www.ruby-forum.com/.
    >


    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
    >



    --
    I always knew that one day Smalltalk would replace Java.
    I just didn't know it would be called Ruby
    -- Kent Beck
     
    Robert Dober, Jul 19, 2007
    #8
  9. Robert Dober wrote:
    On 7/19/07, Stefan Rusterholz <> wrote:
    >> so ._ simulates -> with that hack.


    > 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

    --
    Posted via http://www.ruby-forum.com/.
     
    Stefan Rusterholz, Jul 19, 2007
    #9
  10. Stefan Rusterholz

    Trans Guest

    On Jul 19, 6:31 am, Stefan Rusterholz <> wrote:
    > 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.
     
    Trans, Jul 19, 2007
    #10
  11. Trans wrote:
    > 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

    --
    Posted via http://www.ruby-forum.com/.
     
    Stefan Rusterholz, Jul 19, 2007
    #11
  12. Stefan Rusterholz

    Trans Guest

    On Jul 19, 3:33 pm, Stefan Rusterholz <> wrote:
    > Trans wrote:
    > > 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.)


    I think it rescues all StandardErrors.

    T.
     
    Trans, Jul 20, 2007
    #12
  13. Trans wrote:
    > On Jul 19, 3:33 pm, Stefan Rusterholz <> wrote:
    >> Trans wrote:
    >> > 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.)

    >
    > 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

    --
    Posted via http://www.ruby-forum.com/.
     
    Stefan Rusterholz, Jul 20, 2007
    #13
  14. Stefan Rusterholz

    Eugen Minciu Guest

    Excerpts from Stefan Rusterholz's message of Fri Jul 20 01:06:13 +0300 2007:
    > Robert Dober wrote:
    > On 7/19/07, Stefan Rusterholz <> wrote:
    > >> so ._ simulates -> with that hack.

    >
    > > 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
    >

    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,

    --
    Eugen Minciu.

    Wasting valuable time since 1985.
     
    Eugen Minciu, Jul 20, 2007
    #14
  15. Stefan Rusterholz

    Trans Guest

    On Jul 19, 4:35 pm, "Stephen Duncan" <> wrote:
    > Not sure if the existence of features in other languages is generally
    > considered a good support for RCR's, but Groovy has this feature:http://groovy.codehaus.org/Statements#Statements-Safenavigation(though the
    > syntax can't be borrowed)


    One could ask for something close:

    bar.?.boo.?.quuz

    But that's still pretty ick, though it could do that now with a
    regular method.

    T.
     
    Trans, Jul 20, 2007
    #15
  16. Stefan Rusterholz

    Trans Guest

    On Jul 19, 4:34 pm, Stefan Rusterholz <> wrote:
    > Trans wrote:
    > > On Jul 19, 3:33 pm, Stefan Rusterholz <> wrote:
    > >> Trans wrote:
    > >> > 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.)

    >
    > > 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.


    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.
     
    Trans, Jul 20, 2007
    #16
  17. Nobuyoshi Nakada, Jul 20, 2007
    #17
  18. Stefan Rusterholz, Jul 20, 2007
    #18
  19. Stefan Rusterholz

    Robert Dober Guest

    On 7/20/07, Trans <> wrote:
    >
    >
    > On Jul 19, 4:34 pm, Stefan Rusterholz <> wrote:
    > > Trans wrote:
    > > > On Jul 19, 3:33 pm, Stefan Rusterholz <> wrote:
    > > >> Trans wrote:
    > > >> > 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.)

    > >
    > > > 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.

    >
    > 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


    --
    I always knew that one day Smalltalk would replace Java.
    I just didn't know it would be called Ruby
    -- Kent Beck
     
    Robert Dober, Jul 20, 2007
    #19
  20. Stefan Rusterholz

    Trans Guest

    On Jul 20, 6:29 am, "Robert Dober" <> wrote:
    > On 7/20/07, Trans <> wrote:
    >
    > 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.
     
    Trans, Jul 20, 2007
    #20
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Simon Strandgaard
    Replies:
    1
    Views:
    124
    Simon Strandgaard
    Aug 14, 2004
  2. Brian Candler

    puts nil generates "nil\n"

    Brian Candler, Nov 6, 2004, in forum: Ruby
    Replies:
    1
    Views:
    121
  3. John Carter
    Replies:
    64
    Views:
    692
    Klaus Stein
    May 19, 2005
  4. Simon Strandgaard

    [RCR] nil for unassigned keys

    Simon Strandgaard, Jul 19, 2005, in forum: Ruby
    Replies:
    16
    Views:
    275
    Daniel Brockman
    Jul 19, 2005
  5. Christoffer Sawicki
    Replies:
    5
    Views:
    281
    Christoffer Sawicki
    Sep 2, 2006
Loading...

Share This Page