Hi --

On Sun, 18 Feb 2007, Greg Hurrell wrote:

> I personally would like to see immutable objects like Nil and Fixnum
> return self if sent a dup message.
>
> The problem with the currently model is that it makes it difficult for
> the programmer to make a distinction between "by copy" ("by value")
> and "by reference". The most obvious example to look at is instance
> variables.
>
> There are some cases where you want your instance variables to be set
> "by reference"; that is, where you are interested in a particular,
> specific object and want to keep track of it, including changes in its
> value, over time. Most often the kind of object you want to pass in by
> reference is a high-level object that encapsulates some kind of
> complex state and behaviour.
>
> There are also cases where you want your instance variables to be set
> "by copy"; that is, where you are not concerned about the identity of
> an object and only care about the value of the object at the time you
> assign it to a variable. Most commonly the kinds of objects you'll
> want to pass by copy are simple, primitive objects, usually numbers,
> strings and values like nil.
>
> The trouble is not that Ruby passes everything "by reference" by
> default, but that Ruby makes it hard for you to pass "by copy" ("by
> value") when you want to. Imagine an instance variable for which
> you've defined an accessor using "attr_accessor". By default this will
> pass "by reference".
>
> If you want to pass "by copy" you have to manually write an accessor.
> But if you write your accessor like this:
>
> def my_var=(value)
>  @my_var = value.dup
> end
>
> You'll get exceptions whenever you pass nil (a very common case, I
> would imagine). Change it to this:
>
> def my_var=(value)
>  @my_var = (value.respond_to? :dup ? value.dup : value)
> end
>
> This works for nil, but it's not as readable because of the extra
> punctuation. Try passing in a Fixnum though; you'll get an exception
> because Fixnum claims to respond to "dup" but complains when you
> actually send the message (pretty surprising). So you have to do this:
>
> def my_var=(value)
>  @my_var = value.dup rescue value
> end
>
> To me this seems like an awful lot of work every time you want an
> instance variable to be "by copy" ("by value") instead of "by
> reference". Yes, you'll might have problems here if you pass in a
> singleton-but-mutable object, but I assume that if you know enough
> about what you're doing to specifically want things to be passed in
> "by copy" ("by value") then you also know exactly what will happen
> when try passing in a singleton-but-mutable object.
>
> As a programmer coming from Objective-C one of the current behaviour
> was one the most annoying things about Ruby. Now I just write my
> accessors using "rescue" whenever I want "by copy" behaviour. I
> probably wouldn't have had to adopt this habit if classes like Nil and
> Fixnum just returned self in response to the "dup" message. This is
> the orthodox behaviour in Objective-C; in fact, even singleton-but-
> mutable classes normally just return self if sent the "copy" message.
>
> An even more elegant solution, however, would be to extend
> "attr_accessor" and friends to allow the programmer to specify if
> attributes should be set "by copy" or "by reference". This is exactly
> what the new Obejctive-C 2.0 provides. In those cases where you want
> to override the default behaviour you would do a
> "attr_accessor_bycopy :my_var" and Ruby would do the right thing. Of
> course, there is nothing stopping me from writing my very own
> "attr_accessor_bycopy" method, but it would be nice if it were a
> feature of Ruby itself.

One problem with this is that dup oeprations don't fall precisely
along the lines of a by value/by reference/by copy categorization.  If
you do this:

   a = "hi"
   b = a.dup

you're assigning to b a reference to a dup of a.  If you do this:

   b = a

you're assigning a reference (the one in a) by value to b.  This stuff
won't necessarily transliterate well from another language.

I'd rather just keep what's happening on the surface: if you want a
dup of an object, then call dup on it (with error handling if
necessary).  I think that's better than add a new layer of terminology
to Ruby method names.  (I'm also not sold on the idea of a method
called "dup" harboring the possibility of silently returning something
that isn't a dup, as discussed earlier in the thread.)

Maybe we'll go down the road of flags some day:

   attr_reader(:dup => true)

or something....


David

-- 
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
    (See what readers are saying!  http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)