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.
Cheers,
Greg