Assigning to hash keys when there is a default value?

Can someone explain why there is a difference in the second line of
output for the two hashes:

h = Hash.new(5)

puts h[2]

h[2] ||= 10
p h

···

#----------
puts
#----------

h = Hash.new

puts h[2]

h[2] ||= 10
p h

---output:--
5
{}

nil
{2=>10}
--
Posted via http://www.ruby-forum.com/.

Hi --

Can someone explain why there is a difference in the second line of
output for the two hashes:

h = Hash.new(5)

puts h[2]

h[2] ||= 10
p h

#----------
puts
#----------

h = Hash.new

puts h[2]

h[2] ||= 10
p h

---output:--
5
{}

nil
{2=>10}

x ||= y is, I think, always supposed to be exactly equivalent to
x = x || y, so that line in your first hash should be equivalent to:

   h[2] = 5 || 10

which should assign 5 to h[2]. It looks to me like you've found a bug.
I can't think of any reason (and I really hope there isn't one,
because having an exception to that ||= rule would be very messy) why
using a default hash value would make any difference here. It's still
5 || 10 on the rhs, and it's still just an assignment.

David

···

On Mon, 3 Sep 2007, 7stud -- wrote:

--
* Books:
   RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242\)
   RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
     & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)

unknown wrote:

x ||= y is, I think, always supposed to be exactly equivalent to
x = x || y, so that line in your first hash should be equivalent to:

   h[2] = 5 || 10

which should assign 5 to h[2]. It looks to me like you've found a bug.
I can't think of any reason (and I really hope there isn't one,
because having an exception to that ||= rule would be very messy) why
using a default hash value would make any difference here. It's still
5 || 10 on the rhs, and it's still just an assignment.

I don't think it's a bug; the two examples are set forth on p. 276 of
"The Ruby Way (2nd ed)". However, no explanation is given for why the
two examples work differently.

···

--
Posted via http://www.ruby-forum.com/\.

I can't point my finger on it but I believe x||=y is equivalent to
"x=y unless x" instead of "x=x||y". It seems to be more reasonable to
skip the assignment altogether if the value is true equivalent
already. That would also explain behavior much better. :slight_smile:

Note also:

$ ruby -e 'h=Hash.new 2;set_trace_func lambda {|*a| p a}; h[4]||=10'
["line", "-e", 1, nil, #<Binding:0x1002ff48>, false]
["c-call", "-e", 1, :, #<Binding:0x1002ff0c>, Hash]
["c-call", "-e", 1, :default, #<Binding:0x1002fdf4>, Hash]
["c-return", "-e", 1, :default, #<Binding:0x1002fcf0>, Hash]
["c-return", "-e", 1, :, #<Binding:0x1002fc00>, Hash]

$ ruby -e 'h=Hash.new 2;set_trace_func lambda {|*a| p a}; h[4]=h[4]||10'
["line", "-e", 1, nil, #<Binding:0x1002fee4>, false]
["c-call", "-e", 1, :, #<Binding:0x1002fea8>, Hash]
["c-call", "-e", 1, :default, #<Binding:0x1002fd90>, Hash]
["c-return", "-e", 1, :default, #<Binding:0x1002fc8c>, Hash]
["c-return", "-e", 1, :, #<Binding:0x1002fb9c>, Hash]
["c-call", "-e", 1, :=, #<Binding:0x1002faac>, Hash]
["c-return", "-e", 1, :=, #<Binding:0x1002f9bc>, Hash]

There is no assignment in the first piece.

Kind regards

robert

···

2007/9/3, dblack@wobblini.net <dblack@wobblini.net>:

Hi --

On Mon, 3 Sep 2007, 7stud -- wrote:

> Can someone explain why there is a difference in the second line of
> output for the two hashes:
>
> h = Hash.new(5)
>
> puts h[2]
>
> h[2] ||= 10
> p h
>
> #----------
> puts
> #----------
>
> h = Hash.new
>
> puts h[2]
>
> h[2] ||= 10
> p h
>
> ---output:--
> 5
> {}
>
> nil
> {2=>10}

x ||= y is, I think, always supposed to be exactly equivalent to
x = x || y, so that line in your first hash should be equivalent to:

   h[2] = 5 || 10

which should assign 5 to h[2]. It looks to me like you've found a bug.
I can't think of any reason (and I really hope there isn't one,
because having an exception to that ||= rule would be very messy) why
using a default hash value would make any difference here. It's still
5 || 10 on the rhs, and it's still just an assignment.

# I don't think it's a bug;

dBlack was referring to this:

irb(main):001:0> h = Hash.new 5
=> {}
irb(main):002:0> h[2]
=> 5
irb(main):003:0> h
=> {}
irb(main):004:0> h[2] = h[2]
=> 5
irb(main):005:0> h
=> {2=>5}
irb(main):006:0> 5 || 10
=> 5
irb(main):007:0> h[3] = 5 || 10
=> 5
irb(main):008:0> h
=> {2=>5, 3=>5}
irb(main):009:0> h[4] = h[4] || 10
=> 5
irb(main):010:0> h
=> {2=>5, 3=>5, 4=>5}
irb(main):011:0> h[5] ||= 10
=> 5

after that step above, you must have h[5] or 5=>5

irb(main):012:0> h
=> {2=>5, 3=>5, 4=>5}

sadly none :frowning:

however, the ff works (wc is supposed to be the longcut version for h[5] ||= 10)

irb(main):013:0> h[5] = h[5] || 10
=> 5
irb(main):014:0> h
=> {5=>5, 2=>5, 3=>5, 4=>5}

# the two examples are set forth on p. 276 of
# "The Ruby Way (2nd ed)". However, no explanation is given
# for why the two examples work differently.

they should be different (note, that has nothing to do w the bug dBlack is pointing at) and the output should be,

5
{2=>5} #<---dblack is complaining here because the ruby output is {}

nil
{2=>10}

pls refer to the hash#new document.

irb(main):015:0> system "qri hash.new"
-------------------------------------------------------------- Hash::new
     Hash.new => hash
     Hash.new(obj) => aHash
     Hash.new {|hash, key| block } => aHash

···

From: 7stud -- [mailto:dolgun@excite.com]
------------------------------------------------------------------------
     Returns a new, empty hash. If this hash is subsequently accessed
     by a key that doesn't correspond to a hash entry, the value
     returned depends on the style of new used to create the hash. In
     the first form, the access returns nil. If obj is specified, this
     single object will be used for all default values. If a block is
     specified, it will be called with the hash object and the key, and
     should return the default value. It is the block's responsibility
     to store the value in the hash if required.

        h = Hash.new("Go Fish")
        h["a"] = 100
        h["b"] = 200
        h["a"] #=> 100
        h["c"] #=> "Go Fish"
        # The following alters the single default object
        h["c"].upcase! #=> "GO FISH"
        h["d"] #=> "GO FISH"
        h.keys #=> ["a", "b"]

        # While this creates a new default object each time
        h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" }
        h["c"] #=> "Go Fish: c"
        h["c"].upcase! #=> "GO FISH: C"
        h["d"] #=> "Go Fish: d"
        h.keys #=> ["c", "d"]

kind regards -botp

Hi --

···

On Mon, 3 Sep 2007, 7stud -- wrote:

unknown wrote:

x ||= y is, I think, always supposed to be exactly equivalent to
x = x || y, so that line in your first hash should be equivalent to:

   h[2] = 5 || 10

which should assign 5 to h[2]. It looks to me like you've found a bug.
I can't think of any reason (and I really hope there isn't one,
because having an exception to that ||= rule would be very messy) why
using a default hash value would make any difference here. It's still
5 || 10 on the rhs, and it's still just an assignment.

I don't think it's a bug; the two examples are set forth on p. 276 of
"The Ruby Way (2nd ed)". However, no explanation is given for why the
two examples work differently.

It seems very bug-like to me. I don't know what Hal's take on it is.
Paging Hal....

David

--
* Books:
   RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242\)
   RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
     & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)

Hi --

Hi --

Can someone explain why there is a difference in the second line of
output for the two hashes:

h = Hash.new(5)

puts h[2]

h[2] ||= 10
p h

#----------
puts
#----------

h = Hash.new

puts h[2]

h[2] ||= 10
p h

---output:--
5
{}

nil
{2=>10}

x ||= y is, I think, always supposed to be exactly equivalent to
x = x || y, so that line in your first hash should be equivalent to:

   h[2] = 5 || 10

which should assign 5 to h[2]. It looks to me like you've found a bug.
I can't think of any reason (and I really hope there isn't one,
because having an exception to that ||= rule would be very messy) why
using a default hash value would make any difference here. It's still
5 || 10 on the rhs, and it's still just an assignment.

I can't point my finger on it but I believe x||=y is equivalent to
"x=y unless x" instead of "x=x||y". It seems to be more reasonable to
skip the assignment altogether if the value is true equivalent
already. That would also explain behavior much better. :slight_smile:

I still don't like it. I'm not sure about that "unless" thing.... I
always thought x ||= y was strictly syntactic sugar for x = x || y.
I'm now thinking about how much fun it will be to explain to people
learning Ruby that that's true, unless the lhs is a call to Hash# on
a hash with a default value with a boolean value of true, in which
case...... What fun :slight_smile:

Note also:

$ ruby -e 'h=Hash.new 2;set_trace_func lambda {|*a| p a}; h[4]||=10'
["line", "-e", 1, nil, #<Binding:0x1002ff48>, false]
["c-call", "-e", 1, :, #<Binding:0x1002ff0c>, Hash]
["c-call", "-e", 1, :default, #<Binding:0x1002fdf4>, Hash]
["c-return", "-e", 1, :default, #<Binding:0x1002fcf0>, Hash]
["c-return", "-e", 1, :, #<Binding:0x1002fc00>, Hash]

$ ruby -e 'h=Hash.new 2;set_trace_func lambda {|*a| p a}; h[4]=h[4]||10'
["line", "-e", 1, nil, #<Binding:0x1002fee4>, false]
["c-call", "-e", 1, :, #<Binding:0x1002fea8>, Hash]
["c-call", "-e", 1, :default, #<Binding:0x1002fd90>, Hash]
["c-return", "-e", 1, :default, #<Binding:0x1002fc8c>, Hash]
["c-return", "-e", 1, :, #<Binding:0x1002fb9c>, Hash]
["c-call", "-e", 1, :=, #<Binding:0x1002faac>, Hash]
["c-return", "-e", 1, :=, #<Binding:0x1002f9bc>, Hash]

There is no assignment in the first piece.

I wonder whether there's some special optimization for hashes. I can't
find any other example where the setter method isn't called (except
attr_accessor-created setters, which I haven't puzzled through yet). I
won't paste the whole output but run this:

class C
   def x; @x; end
   def x=(x); @x = x; end
end

c = C.new

set_trace_func lambda {|*a| p a }
c.x ||= 1
puts
c.x = nil
puts
c.x = c.x || 1
puts

a =
puts
a[1] ||= 1
puts
a[2] = a[2] || 1

As far as I can tell, the sugar version and the inlined version work
exactly the same, except for hashes.

My vote is still for bug :slight_smile:

David

···

On Mon, 3 Sep 2007, Robert Klemme wrote:

2007/9/3, dblack@wobblini.net <dblack@wobblini.net>:

On Mon, 3 Sep 2007, 7stud -- wrote:

--
* Books:
   RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242\)
   RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
     & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)

Robert,

Although I can't find the documentation quickly, although I'm 95%
certain that it should be in the pickaxe somewhere, I'm pretty sure
that you are correct.

I've just looked at parse.y and eval.c for ruby1.8.6 and it would appear that:

  h[2] ||= 10

gets compiled to a NODE_OP_ASGN_OR node with h[2] as the lhs and 10 as
the rhs. Here's the code from eval.c which evaluates such a node:

      case NODE_OP_ASGN_OR:
  if ((node->nd_aid && !is_defined(self, node->nd_head, 0)) ||
      !RTEST(result = rb_eval(self, node->nd_head))) {
      node = node->nd_value;
      goto again;
  }
  break;

So what happens is that the lhs is only evaluated if the the lhs
(node->nd_head) is not defined || it evaluates to an untrue value.

In the case of h[5] the default value for the hash means that it will
evaluate to 5, and the assignment is not done.

I for one, am glad that it works this way. The ruby idiom

  x ||= y

is heavily used for lazy initialization/caching. While most often,
it's the rhs which is expensive to compute and therefore the thing we
want to short-circuit, since x= can in general be a method, and might
just be expensive, then optimizing the case where it boils down to x =
x as a nop, makes sense.

···

On 9/3/07, Robert Klemme <shortcutter@googlemail.com> wrote:

2007/9/3, dblack@wobblini.net <dblack@wobblini.net>:

> x ||= y is, I think, always supposed to be exactly equivalent to
> x = x || y, ...

I can't point my finger on it but I believe x||=y is equivalent to
"x=y unless x" instead of "x=x||y". It seems to be more reasonable to
skip the assignment altogether if the value is true equivalent
already. That would also explain behavior much better. :slight_smile:

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Peña, Botp wrote:

they should be different (note, that has nothing to do w the bug dBlack
is pointing at) and the output should be,

5
{2=>5} #<---dblack is complaining here because the ruby output is {}

nil
{2=>10}

Yes. That is what I am complaining about too! Your are right: the
second line for each section of the output should be different--I
mispoke. What I want to know is why the the output is {} and not
{2=>5}.

The author of "The Ruby Way (2nd ed)" presents the two examples as well
as the different output for each example, yet he doesn't explain the
reason for the different output. He suggests that you can use ||= to
selectively assign values only to keys that don't exist. The point I
think he was trying to make with the two examples is that when you have
a default value for non-existent keys, all keys "exist", so ||= will not
create new keys with the default value.

···

--
Posted via http://www.ruby-forum.com/\.

Peña, Botp wrote:

pls refer to the hash#new document.

I also have "Programming Ruby (2nd ed)", and I read the documentation
therein for Hash.new many times before posting--which is the same
documentation that ri produces.

···

--
Posted via http://www.ruby-forum.com/\.

I always wondered why JavaScript is missing the '||='
operator (I rarely need a "&&=" one). Maybe Brendan
was prescient enough to realize an r-value interpretation
would lead to ambiguity. Maybe, but I still say
it's a bug. +1 for DavidB.

- Eric

Hi --

x ||= y is, I think, always supposed to be exactly equivalent to
x = x || y, ...

I can't point my finger on it but I believe x||=y is equivalent to
"x=y unless x" instead of "x=x||y". It seems to be more reasonable to
skip the assignment altogether if the value is true equivalent
already. That would also explain behavior much better. :slight_smile:

Robert,

Although I can't find the documentation quickly, although I'm 95%
certain that it should be in the pickaxe somewhere, I'm pretty sure
that you are correct.

I've just looked at parse.y and eval.c for ruby1.8.6 and it would appear that:

h[2] ||= 10

gets compiled to a NODE_OP_ASGN_OR node with h[2] as the lhs and 10 as
the rhs. Here's the code from eval.c which evaluates such a node:

     case NODE_OP_ASGN_OR:
  if ((node->nd_aid && !is_defined(self, node->nd_head, 0)) ||
      !RTEST(result = rb_eval(self, node->nd_head))) {
      node = node->nd_value;
      goto again;
  }
  break;

So what happens is that the lhs is only evaluated if the the lhs
(node->nd_head) is not defined || it evaluates to an untrue value.

In the case of h[5] the default value for the hash means that it will
evaluate to 5, and the assignment is not done.

I for one, am glad that it works this way. The ruby idiom

x ||= y

is heavily used for lazy initialization/caching. While most often,
it's the rhs which is expensive to compute and therefore the thing we
want to short-circuit, since x= can in general be a method, and might
just be expensive, then optimizing the case where it boils down to x =
x as a nop, makes sense.

I think The method always gets called, though:

   class C
     attr_reader :x
     def x=(n)
       puts "C#x="
       true
     end
   end

   c = C.new
   c.x ||= 3 # C#x=

*Unless*, of course, the object is a Hash which has either (a) a key
corresponding to the indicated value, or (b) a default value with
boolean truth value.

Sigh. I really wish it were otherwise. What an annoying exception to
the rule.

Also, in the famous:

   h = Hash.new(1)
   h[5] ||= 10

case, it definitely isn't doing the "x = x" equivalent, since that
would set the 5 key to 1.

I don't know.... However many times I look at it, I just can't see
this:

   h[5] ||= 10

as *not* meaning that I expect h to end up having a 5 key, one way or
another.

David

···

On Wed, 5 Sep 2007, Rick DeNatale wrote:

On 9/3/07, Robert Klemme <shortcutter@googlemail.com> wrote:

2007/9/3, dblack@wobblini.net <dblack@wobblini.net>:

--
* Books:
   RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242\)
   RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
     & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)

Hi --

Peña, Botp wrote:

they should be different (note, that has nothing to do w the bug dBlack
is pointing at) and the output should be,

5
{2=>5} #<---dblack is complaining here because the ruby output is {}

nil
{2=>10}

Yes. That is what I am complaining about too! Your are right: the
second line for each section of the output should be different--I
mispoke. What I want to know is why the the output is {} and not
{2=>5}.

The author of "The Ruby Way (2nd ed)" presents the two examples as well
as the different output for each example, yet he doesn't explain the
reason for the different output. He suggests that you can use ||= to
selectively assign values only to keys that don't exist. The point I
think he was trying to make with the two examples is that when you have
a default value for non-existent keys, all keys "exist", so ||= will not
create new keys with the default value.

I don't have the book at hand but the default value definitely doesn't
mean that all keys exist (or even "exist" :slight_smile:

irb(main):003:0> h = Hash.new(5)
=> {}
irb(main):004:0> h[1]
=> 5
irb(main):005:0> h.has_key?(1)
=> false

The only possible explanation I can come up with, which I don't like,
is that:

   h[2] ||= 10

is being treated like:

   5 = 5 || 10

which is just another can of worms.

David

···

On Mon, 3 Sep 2007, 7stud -- wrote:

--
* Books:
   RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242\)
   RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
     & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)

I don't think this is a bug, kittens. since h[2] returns a value [even
though it's not set], it causes h[2] to evaluate so the assignment
never happens. x ||= y just means give me x or set x to y if there's
no value for x. h[2] _does_ have a value if only a default one.

just my two cents.

unless i missed the point here, in which case i apologize for my hasty
conclusion. :))

RSL

···

On 9/3/07, 7stud -- <dolgun@excite.com> wrote:

Peña, Botp wrote:
> pls refer to the hash#new document.
>

I also have "Programming Ruby (2nd ed)", and I read the documentation
therein for Hash.new many times before posting--which is the same
documentation that ri produces.

--
Posted via http://www.ruby-forum.com/\.

Hi --

>
>>> x ||= y is, I think, always supposed to be exactly equivalent to
>>> x = x || y, ...
>
>> I can't point my finger on it but I believe x||=y is equivalent to
>> "x=y unless x" instead of "x=x||y". It seems to be more reasonable to
>> skip the assignment altogether if the value is true equivalent
>> already. That would also explain behavior much better. :slight_smile:
>
> Robert,
>
> Although I can't find the documentation quickly, although I'm 95%
> certain that it should be in the pickaxe somewhere, I'm pretty sure
> that you are correct.
>
> I've just looked at parse.y and eval.c for ruby1.8.6 and it would appear that:
>
> h[2] ||= 10
>
> gets compiled to a NODE_OP_ASGN_OR node with h[2] as the lhs and 10 as
> the rhs. Here's the code from eval.c which evaluates such a node:
>
> case NODE_OP_ASGN_OR:
> if ((node->nd_aid && !is_defined(self, node->nd_head, 0)) ||
> !RTEST(result = rb_eval(self, node->nd_head))) {
> node = node->nd_value;
> goto again;
> }
> break;
>
> So what happens is that the lhs is only evaluated if the the lhs
> (node->nd_head) is not defined || it evaluates to an untrue value.
>
> In the case of h[5] the default value for the hash means that it will
> evaluate to 5, and the assignment is not done.
>
> I for one, am glad that it works this way. The ruby idiom
>
> x ||= y
>
> is heavily used for lazy initialization/caching. While most often,
> it's the rhs which is expensive to compute and therefore the thing we
> want to short-circuit, since x= can in general be a method, and might
> just be expensive, then optimizing the case where it boils down to x =
> x as a nop, makes sense.

I think The method always gets called, though:

   class C
     attr_reader :x
     def x=(n)
       puts "C#x="
       true
     end
   end

   c = C.new
   c.x ||= 3 # C#x=

*Unless*, of course, the object is a Hash which has either (a) a key
corresponding to the indicated value, or (b) a default value with
boolean truth value.

No, in the case you posited, the assignment happened and C#x= got
called because c.x returned nil.

irb(main):001:0> class D
irb(main):002:1> def x
irb(main):003:2> @x || 5
irb(main):004:2> end
irb(main):005:1> def x=(v)
irb(main):006:2> puts "x=called"
irb(main):007:2> @x = v
irb(main):008:2> end
irb(main):009:1> end
=> nil
irb(main):010:0> d = D.new
=> #<D:0xb7b47528>
irb(main):011:0> d.x ||= 10
=> 5
irb(main):012:0> d.x
=> 5
irb(main):013:0> d.x=10
x=called
=> 10
irb(main):014:0> d.x
=> 10
irb(main):015:0>

Sigh. I really wish it were otherwise. What an annoying exception to
the rule.

Except that the 'rule' wasn't as you thought. The rule is that

    x ||= y

is the same as

   x = y unless x

Also, in the famous:

   h = Hash.new(1)
   h[5] ||= 10

case, it definitely isn't doing the "x = x" equivalent, since that
would set the 5 key to 1.

And that's as expected because h[5] returns the default value and
doesn't affect the state of the hash a whit, it doesn't create a 5
key. If you want the default to affect the hash you need something
like

  hsh = Hash.new {|h,k| h[k] = 10}

$ fri Hash.new
-------------------------------------------------------------- Hash::new
     Hash.new => hash
     Hash.new(obj) => aHash
     Hash.new {|hash, key| block } => aHash

···

On 9/4/07, dblack@wobblini.net <dblack@wobblini.net> wrote:

On Wed, 5 Sep 2007, Rick DeNatale wrote:
> On 9/3/07, Robert Klemme <shortcutter@googlemail.com> wrote:
>> 2007/9/3, dblack@wobblini.net <dblack@wobblini.net>:

------------------------------------------------------------------------
     Returns a new, empty hash. If this hash is subsequently accessed
     by a key that doesn't correspond to a hash entry, the value
     returned depends on the style of new used to create the hash. In
     the first form, the access returns nil. If obj is specified, this
     single object will be used for all default values. If a block is
     specified, it will be called with the hash object and the key, and
     should return the default value. It is the block's responsibility
     to store the value in the hash if required.

        h = Hash.new("Go Fish")
        h["a"] = 100
        h["b"] = 200
        h["a"] #=> 100
        h["c"] #=> "Go Fish"
        # The following alters the single default object
        h["c"].upcase! #=> "GO FISH"
        h["d"] #=> "GO FISH"
        h.keys #=> ["a", "b"]

Note the value of h.keys at the end of the RI example.

I don't know.... However many times I look at it, I just can't see
this:

   h[5] ||= 10

as *not* meaning that I expect h to end up having a 5 key, one way or
another.

Maybe just one more try?! <G>
--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

unknown wrote:

The only possible explanation I can come up with, which I don't like,
is that:

   h[2] ||= 10

is being treated like:

   5 = 5 || 10

which is just another can of worms.

I also considered that possibility. Does the trace output support that?
I don't really know how to read the trace output, but it doesn't look
like there are two calls to Hash# for each ||=.

···

--
Posted via http://www.ruby-forum.com/\.

Russell Norris wrote:

x ||= y just means give me x or set x to y if there's no value for x.

Maybe it does, but if we follow the logic that x op= y is a shortcut for
x = x op y, what it *should* mean is: set x to y if there's no value for
x, otherwise set it to itself.

···

--
NP: Graveworm - I - The Machine
Jabber: sepp2k@jabber.org
ICQ: 205544826

Hi --

I don't think this is a bug, kittens. since h[2] returns a value [even
though it's not set], it causes h[2] to evaluate so the assignment
never happens. x ||= y just means give me x or set x to y if there's
no value for x. h[2] _does_ have a value if only a default one.

just my two cents.

unless i missed the point here, in which case i apologize for my hasty
conclusion. :))

The thing is, this:

   x ||= y

always means (at least, so I've always been told):

   x = x || y

which in the case of the hash with a default value of 5, would mean:

   h[2] = 5 || 10

In other words: h.=(2, 5||10)

At that point, the default value is out of the picture. The default
value has no implications for the #= (writer) method; it's only what
you get when you use the # (reader) method.

So what's at stake here is the matter of x ||= y behaving predictably.
I have to say, it's a case where I'm not even really concerned with
the implementation (e.g., whether it uses on its way to =) but
just the semantics. I don't like the idea that the syntactic sugar is
actually not a reliable drop-in replacement for the thing it's
sugaring.

David

···

On Mon, 3 Sep 2007, Russell Norris wrote:

--
* Books:
   RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242\)
   RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
     & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)

I don't want to believe that because it raises the question of why

    h[2] = 10

isn't treated as

    5 = 10

I mean, I simply can't believe the Ruby interpreter would treat h[2] as a simple rvalue in h[2] ||= 10.

Robert Klemme explanation is more believable although I find it somewhat upsetting after years of believing x =<operator> y is just syntactic sugar for x = x <operator> y. The optimization he describes is a plausible one, and I can't think of any other case where it would backfire.

Regards, Morton

···

On Sep 3, 2007, at 7:37 AM, dblack@wobblini.net wrote:

The only possible explanation I can come up with, which I don't like,
is that:

  h[2] ||= 10

is being treated like:

  5 = 5 || 10

which is just another can of worms.

I don't like the idea that the syntactic sugar is
actually not a reliable drop-in replacement for the thing it's
sugaring.

Apparently, ||= is sugar free.

···

--
Posted via http://www.ruby-forum.com/\.