'=||'

Discussion in 'Ruby' started by James Byrne, May 5, 2009.

  1. James Byrne

    James Byrne Guest

    Can someone point out to me where exactly in the API I find a discussion
    of the '=||' operator? Or am I imagining things and this does not
    really exist?
    --
    Posted via http://www.ruby-forum.com/.
    James Byrne, May 5, 2009
    #1
    1. Advertising

  2. On 5 May 2009, at 20:51, James Byrne wrote:
    > Can someone point out to me where exactly in the API I find a
    > discussion
    > of the '=||' operator? Or am I imagining things and this does not
    > really exist?



    Are you referring to ||= ? If so it's one of the augmented assignment
    operators so you won't find it documented separately as it's syntactic
    sugar for:

    x = x || some_other_value


    Ellie

    Eleanor McHugh
    Games With Brains
    http://slides.games-with-brains.net
    ----
    raise ArgumentError unless @reality.responds_to? :reason
    Eleanor McHugh, May 5, 2009
    #2
    1. Advertising

  3. James Byrne

    James Byrne Guest

    James Byrne wrote:
    > Or am I imagining things and this does not really exist?


    Maybe I should look for '||=' instead. Sorry.

    --
    Posted via http://www.ruby-forum.com/.
    James Byrne, May 5, 2009
    #3
  4. Here's a link for Ruby's operator expressions

    http://phrogz.net/ProgrammingRuby/language.html

    Joseph Gutierrez
    Web Developer - Inc21




    Eleanor McHugh wrote:
    > On 5 May 2009, at 20:51, James Byrne wrote:
    >> Can someone point out to me where exactly in the API I find a discussion
    >> of the '=||' operator? Or am I imagining things and this does not
    >> really exist?

    >
    >
    > Are you referring to ||= ? If so it's one of the augmented assignment
    > operators so you won't find it documented separately as it's syntactic
    > sugar for:
    >
    > x = x || some_other_value
    >
    >
    > Ellie
    >
    > Eleanor McHugh
    > Games With Brains
    > http://slides.games-with-brains.net
    > ----
    > raise ArgumentError unless @reality.responds_to? :reason
    >
    >
    >
    >


    --
    This message has been scanned for viruses and
    dangerous content by MailScanner, and is
    believed to be clean.
    Joe Gutierrez, May 5, 2009
    #4
  5. James Byrne

    7stud -- Guest

    Eleanor McHugh wrote:
    > Are you referring to ||= ? If so it's one of the augmented assignment
    > operators so you won't find it documented separately as it's syntactic
    > sugar for:
    >
    > x = x || some_other_value
    >
    >


    Nope.

    h = Hash.new(10)

    h["red"] = h["red"] || 20

    --output:--
    {"red"=>10}


    h = Hash.new(10)
    h["blue"] ||= 20
    p h

    --output:--
    {}


    The statement:

    x ||= val

    is actually equivalent to:

    x = val unless x

    --
    Posted via http://www.ruby-forum.com/.
    7stud --, May 6, 2009
    #5
  6. On 6 May 2009, at 00:09, 7stud -- wrote:
    > Eleanor McHugh wrote:
    >> Are you referring to ||= ? If so it's one of the augmented assignment
    >> operators so you won't find it documented separately as it's
    >> syntactic
    >> sugar for:
    >>
    >> x = x || some_other_value

    >
    > Nope.
    >
    > h = Hash.new(10)
    >
    > h["red"] = h["red"] || 20
    >
    > --output:--
    > {"red"=>10}
    >
    >
    > h = Hash.new(10)
    > h["blue"] ||= 20
    > p h
    >
    > --output:--
    > {}
    >
    >
    > The statement:
    >
    > x ||= val
    >
    > is actually equivalent to:
    >
    > x = val unless x



    It seems you've misunderstood what happens under the hood when using
    augmented assignment with tables as '||=' then becomes syntactic sugar
    for 'x[] = x[] || some_other_value' and the assignment is performed
    via '[]=' rather than '='. '[]=' will not create a key if it believes
    it already exists and this is the cause of the behaviour you're seeing.

    h = Hash.new(10)
    p h["blue"] => 10
    h["blue"] ||= 20
    p h => {}

    In this case when '||=' invokes the assignment it finds that h["blue"]
    already contains a value because of the default so the hash method
    '[]=' doesn't attempt to create a new key because it appears that the
    key already exists.

    Contrast this to:

    h = Hash.new(10)
    p h["blue"] => 10
    h["blue"] = nil
    p h => { "blue" => nil }
    h["blue"] ||= 10
    p h => { "blue" => 10 }
    h["blue"] ||= 20
    p h => { "blue" => 10 }

    Here the key has been explicitly set equal to nil and '||=' acts the
    way we'd expect an augmented assignment to work with scalar types.

    Finally if no default is set for the table:

    h = {}
    h["red"] ||= 10
    p h => {"red" => 10}

    The key is always created as expected.


    Ellie

    Eleanor McHugh
    Games With Brains
    http://slides.games-with-brains.net
    ----
    raise ArgumentError unless @reality.responds_to? :reason
    Eleanor McHugh, May 6, 2009
    #6
  7. On Tue, May 5, 2009 at 8:45 PM, Eleanor McHugh
    <> wrote:
    > On 6 May 2009, at 00:09, 7stud -- wrote:
    >>
    >> Eleanor McHugh wrote:
    >>>
    >>> Are you referring to ||= ? If so it's one of the augmented assignment
    >>> operators so you won't find it documented separately as it's syntactic
    >>> sugar for:
    >>>
    >>> x = x || some_other_value

    >>
    >> Nope.

    ...
    >> The statement:
    >>
    >> x ||= val
    >>
    >> is actually equivalent to:
    >>
    >> x = val unless x

    >
    >
    > It seems you've misunderstood what happens under the hood when using
    > augmented assignment with tables as '||=' then becomes syntactic sugar for
    > 'x[] = x[] || some_other_value' and the assignment is performed via '[]='
    > rather than '='. '[]=' will not create a key if it believes it already
    > exists and this is the cause of the behaviour you're seeing.


    No, if x is truthy then

    x ||= expression

    will NOT do any assignment.

    The real equivalent to x ||= y

    is

    x || x = y

    The assignment is short-circuited.

    For the proof see:

    http://talklikeaduck.denhaven2.com/2008/04/26/x-y-redux


    --
    Rick DeNatale

    Blog: http://talklikeaduck.denhaven2.com/
    Twitter: http://twitter.com/RickDeNatale
    WWR: http://www.workingwithrails.com/person/9021-rick-denatale
    LinkedIn: http://www.linkedin.com/in/rickdenatale
    Rick DeNatale, May 6, 2009
    #7
  8. James Byrne

    7stud -- Guest

    Eleanor McHugh wrote:
    > On 6 May 2009, at 00:09, 7stud -- wrote:
    >> h = Hash.new(10)
    >>
    >> x = val unless x

    > It seems you've misunderstood what happens under the hood when using
    > augmented assignment with tables as '||=' then becomes syntactic sugar
    > for 'x[] = x[] || some_other_value' and the assignment is performed
    > via '[]=' rather than '='. '[]=' will not create a key if it believes
    > it already exists and this is the cause of the behaviour you're seeing.
    >
    > h = Hash.new(10)
    > p h["blue"] => 10
    > h["blue"] ||= 20
    > p h => {}
    >
    > In this case when '||=' invokes the assignment it finds that h["blue"]
    > already contains a value because of the default so the hash method
    > '[]=' doesn't attempt to create a new key because it appears that the
    > key already exists.
    >
    > Contrast this to:
    >
    > h = Hash.new(10)
    > p h["blue"] => 10
    > h["blue"] = nil
    > p h => { "blue" => nil }
    > h["blue"] ||= 10
    > p h => { "blue" => 10 }
    > h["blue"] ||= 20
    > p h => { "blue" => 10 }
    >
    > Here the key has been explicitly set equal to nil and '||=' acts the
    > way we'd expect an augmented assignment to work with scalar types.
    >
    > Finally if no default is set for the table:
    >
    > h = {}
    > h["red"] ||= 10
    > p h => {"red" => 10}
    >
    > The key is always created as expected.
    >



    Why do I need to care about what's going on under the hood? If two
    things
    produce different results, then how can one be considered syntactic
    sugar for the other. In my opinion, "syntactic sugar" means that the
    two formats can
    be used interchangeably--with experienced programmers naturally
    gravitating to the shorter, easier to type format, and inexperienced
    programmers preferring the longer, but easier to understand format.

    --
    Posted via http://www.ruby-forum.com/.
    7stud --, May 6, 2009
    #8
  9. On 6 May 2009, at 02:38, 7stud -- wrote:
    > Why do I need to care about what's going on under the hood?


    Because when things don't work the way you expect, looking under the
    hood allows you to adjust your expectations ;)

    > If two
    > things
    > produce different results, then how can one be considered syntactic
    > sugar for the other. In my opinion, "syntactic sugar" means that the
    > two formats can
    > be used interchangeably--with experienced programmers naturally
    > gravitating to the shorter, easier to type format, and inexperienced
    > programmers preferring the longer, but easier to understand format.


    All that syntactic sugar means is that one phrase is equivalent to
    another syntactically, not that they're semantically equivalent.
    However in the case of 'x = x || y' and 'x[n] = x[n] || y' are they
    even syntactically equivalent? No. They appear syntactically similar,
    but one invokes the '=' assignment operator whereas the other sends
    the '[]=' message to the receiver. And because '[]=' leaves the
    semantics of assignment to the receiver, that allows for the
    differences you're seeing in this case.


    Ellie

    Eleanor McHugh
    Games With Brains
    http://slides.games-with-brains.net
    ----
    raise ArgumentError unless @reality.responds_to? :reason
    Eleanor McHugh, May 6, 2009
    #9
  10. On 6 May 2009, at 02:31, Rick DeNatale wrote:
    > On Tue, May 5, 2009 at 8:45 PM, Eleanor McHugh
    > <> wrote:
    >> On 6 May 2009, at 00:09, 7stud -- wrote:
    >>> The statement:
    >>>
    >>> x ||= val
    >>>
    >>> is actually equivalent to:
    >>>
    >>> x = val unless x

    >>
    >>
    >> It seems you've misunderstood what happens under the hood when using
    >> augmented assignment with tables as '||=' then becomes syntactic
    >> sugar for
    >> 'x[] = x[] || some_other_value' and the assignment is performed via
    >> '[]='
    >> rather than '='. '[]=' will not create a key if it believes it
    >> already
    >> exists and this is the cause of the behaviour you're seeing.

    >
    > No, if x is truthy then
    >
    > x ||= expression
    >
    > will NOT do any assignment.
    >
    > The real equivalent to x ||= y
    >
    > is
    >
    > x || x = y
    >
    > The assignment is short-circuited.
    >
    > For the proof see:
    >
    > http://talklikeaduck.denhaven2.com/2008/04/26/x-y-redux


    Yes, for assignment that's the case. But 'x[n] ||= y' isn't an
    instance of assignment in that case as can easily be demonstrated:

    class Test
    def method_missing symbol, *args
    puts "calling method #{symbol}"
    end
    end

    t = Test.new
    t[:a] ||= 17

    output: calling method []
    calling method []=

    Notice how even though method_missing returns a value and is thus
    'true' the sequence still attempts all parts of the expression, and

    t[:a] = t[:a] || 17

    output: calling method []
    calling method []=
    => 17

    confirms that no short-circuited evaluation occurs.


    Ellie

    Eleanor McHugh
    Games With Brains
    http://slides.games-with-brains.net
    ----
    raise ArgumentError unless @reality.responds_to? :reason
    Eleanor McHugh, May 6, 2009
    #10
  11. On Wed, May 6, 2009 at 6:54 AM, Eleanor McHugh
    <> wrote:
    > On 6 May 2009, at 02:31, Rick DeNatale wrote:
    >>
    >> On Tue, May 5, 2009 at 8:45 PM, Eleanor McHugh
    >> <> wrote:
    >>>
    >>> On 6 May 2009, at 00:09, 7stud -- wrote:
    >>>>
    >>>> The statement:
    >>>>
    >>>> x ||=3D val
    >>>>
    >>>> is actually equivalent to:
    >>>>
    >>>> x =3D val unless x
    >>>
    >>>
    >>> It seems you've misunderstood what happens under the hood when using
    >>> augmented assignment with tables as '||=3D' then becomes syntactic suga=

    r
    >>> for
    >>> 'x[] =3D x[] || some_other_value' and the assignment is performed via '=

    []=3D'
    >>> rather than '=3D'. '[]=3D' will not create a key if it believes it alre=

    ady
    >>> exists and this is the cause of the behaviour you're seeing.

    >>
    >> No, if x is truthy then
    >>
    >> =A0x ||=3D expression
    >>
    >> will NOT do any assignment.
    >>
    >> The real equivalent to x ||=3D y
    >>
    >> is
    >>
    >> x || x =3D y
    >>
    >> The assignment is short-circuited.
    >>
    >> For the proof see:
    >>
    >> http://talklikeaduck.denhaven2.com/2008/04/26/x-y-redux

    >
    > Yes, for assignment that's the case. But 'x[n] ||=3D y' isn't an instance=

    of
    > assignment in that case as can easily be demonstrated:
    >
    > class Test
    > =A0def method_missing symbol, *args
    > =A0 =A0puts "calling method #{symbol}"
    > =A0end
    > end
    >
    > t =3D Test.new
    > t[:a] ||=3D 17
    >
    > output: calling method []
    > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0calling method []=3D
    >
    > Notice how even though method_missing returns a value and is thus 'true' =

    the
    > sequence still attempts all parts of the expression, and
    >
    > t[:a] =3D t[:a] || 17
    >
    > output: calling method []
    > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0calling method []=3D
    > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0=3D> 17
    >
    > confirms that no short-circuited evaluation occurs.
    >


    But this is because the call to the missing [] method goes to the
    method missing method which returns nil.

    Remember that the assertion is that

    t[:a] ||=3D 17

    is equivalent to

    (t[:a]) || (t[:a] =3D 17)

    which in turn is equivalent to:

    ((t.[]:)a)) || (t.[]=3D:)a, 17))

    The output of your example shows both the :[] and :[]=3D methods are being =
    sent.

    Try this variant:

    class Test
    def method_missing symbol, *args
    puts "calling method #{symbol}"
    end

    def [](a)
    puts "in [] method"
    a
    end
    end

    t =3D Test.new
    t[:a] ||=3D 17

    This produces the output;
    in [] method

    The short circuiting only happens if the lhs expression returns a
    non-truthy value.

    Now it's true (I think) that "a op=3D b" is the same as "a =3D a op b" in
    C, Ruby ain't C.

    Sometimes the stuff "under the hood" is a little more complicated than
    it first appears. <G>

    --=20
    Rick DeNatale

    Blog: http://talklikeaduck.denhaven2.com/
    Twitter: http://twitter.com/RickDeNatale
    WWR: http://www.workingwithrails.com/person/9021-rick-denatale
    LinkedIn: http://www.linkedin.com/in/rickdenatale
    Rick DeNatale, May 6, 2009
    #11
  12. On Wed, May 6, 2009 at 6:54 AM, Eleanor McHugh
    <> wrote:
    > On 6 May 2009, at 02:31, Rick DeNatale wrote:
    >>
    >> On Tue, May 5, 2009 at 8:45 PM, Eleanor McHugh
    >> <> wrote:
    >>>

    >
    > Notice how even though method_missing returns a value and is thus 'true' the
    > sequence still attempts all parts of the expression, and


    I should have mentioned in my reply that since your method missing
    body consists of a single puts statement, and since puts returns nil,
    that value is non-truthy

    and

    nil || x

    is x which is why the []= gets sent in your example.

    --
    Rick DeNatale

    Blog: http://talklikeaduck.denhaven2.com/
    Twitter: http://twitter.com/RickDeNatale
    WWR: http://www.workingwithrails.com/person/9021-rick-denatale
    LinkedIn: http://www.linkedin.com/in/rickdenatale
    Rick DeNatale, May 6, 2009
    #12
  13. On 6 May 2009, at 13:03, Rick DeNatale wrote:
    > The short circuiting only happens if the lhs expression returns a
    > non-truthy value.
    >
    > Now it's true (I think) that "a op= b" is the same as "a = a op b" in
    > C, Ruby ain't C.


    :)

    > Sometimes the stuff "under the hood" is a little more complicated than
    > it first appears. <G>



    Very true, and we all learn new stuff by considering it. For example I
    suspect quite a few people would be surprised by the following code:

    class Test
    def initialize
    @switch = false
    end

    def [] x
    puts "[#{x}]"
    @switch = !@switch
    end

    def []= x, y
    puts "[#{x}] = #{y}"
    end
    end

    t = Test.new
    t[:a] ||= 17
    t[:a] ||= 17
    t[:a] ||= 17

    output: [a]
    [a]
    [a]

    here the '[]' call returns 'true' and 'false' by turns but in either
    event the assignment fails to call '[]='...


    Ellie

    Eleanor McHugh
    Games With Brains
    http://slides.games-with-brains.net
    ----
    raise ArgumentError unless @reality.responds_to? :reason
    Eleanor McHugh, May 6, 2009
    #13
  14. On 6 May 2009, at 12:50, Shot (Piotr Szotkowski) wrote:
    > Eleanor McHugh:


    > No, puts returns nil.


    doh! I knew that :(


    Ellie

    Eleanor McHugh
    Games With Brains
    http://slides.games-with-brains.net
    ----
    raise ArgumentError unless @reality.responds_to? :reason
    Eleanor McHugh, May 6, 2009
    #14
  15. On 6 May 2009, at 13:05, Rick DeNatale wrote:
    > I should have mentioned in my reply that since your method missing
    > body consists of a single puts statement, and since puts returns nil,
    > that value is non-truthy
    >
    > and
    >
    > nil || x
    >
    > is x which is why the []= gets sent in your example.


    Perhaps I should have paid more attention to my morning email spam
    regarding 'retrograde mercury'...


    Ellie

    Eleanor McHugh
    Games With Brains
    http://slides.games-with-brains.net
    ----
    raise ArgumentError unless @reality.responds_to? :reason
    Eleanor McHugh, May 6, 2009
    #15
  16. On Wed, May 6, 2009 at 9:43 AM, Eleanor McHugh
    <> wrote:
    > On 6 May 2009, at 13:03, Rick DeNatale wrote:
    >>
    >> The short circuiting only happens if the lhs expression returns a
    >> non-truthy value.
    >>
    >> Now it's true (I think) that "a op=3D b" is the same as "a =3D a op b" i=

    n
    >> C, Ruby ain't C.

    >
    > :)
    >
    >> Sometimes the stuff "under the hood" is a little more complicated than
    >> it first appears. <G>

    >
    >
    > Very true, and we all learn new stuff by considering it. For example I
    > suspect quite a few people would be surprised by the following code:
    >
    > class Test
    > =A0def initialize
    > =A0 =A0@switch =3D false
    > =A0end
    >
    > =A0def [] x
    > =A0 =A0puts "[#{x}]"
    > =A0 =A0@switch =3D !@switch
    > =A0end
    >
    > =A0def []=3D x, y
    > =A0 =A0puts "[#{x}] =3D #{y}"
    > =A0end
    > end
    >
    > t =3D Test.new
    > t[:a] ||=3D 17
    > t[:a] ||=3D 17
    > t[:a] ||=3D 17
    >
    > output: =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 [a]
    > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0[a]
    > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0[a]
    >
    > here the '[]' call returns 'true' and 'false' by turns but in either even=

    t
    > the assignment fails to call '[]=3D'...



    Are you sure? When I run your code I get this:

    [a]
    [a]
    [a] =3D 17
    [a]



    --=20
    Rick DeNatale

    Blog: http://talklikeaduck.denhaven2.com/
    Twitter: http://twitter.com/RickDeNatale
    WWR: http://www.workingwithrails.com/person/9021-rick-denatale
    LinkedIn: http://www.linkedin.com/in/rickdenatale
    Rick DeNatale, May 6, 2009
    #16
  17. On 6 May 2009, at 14:59, Rick DeNatale wrote:
    > Are you sure? When I run your code I get this:
    >
    > [a]
    > [a]
    > [a] = 17
    > [a]


    I typed '||' for '||=' when I ran that in irb, hence the 'mercury
    retrograde' comment elsewhere...
    Anyway it seems I'm suffering from an IAK meltdown today so it's
    probably best if I step away from the keyboard before any more
    innocent bytes get hurt :)


    Ellie

    Eleanor McHugh
    Games With Brains
    http://slides.games-with-brains.net
    ----
    raise ArgumentError unless @reality.responds_to? :reason
    Eleanor McHugh, May 6, 2009
    #17
    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.

Share This Page