From: "David A. Black" Date: 2008-09-09T00:12:00+09:00 Subject: Re: "Pointer" to an object? (i.e. reflect changes to original) Hi -- On Mon, 8 Sep 2008, Doug Glidden wrote: > Hi, > > I've already made a post similar to this on the Rails list, so I > apologize to anyone who subscribes to both for being repetitive. From > what I've read, it seems to be impossible to create "pointers" in Ruby, > such as the following pseudocode: > > string = 'a string' > copy = *string > string = 'a different string' # both string and copy are changed Assignment means breaking the binding between the identifier and the (reference to an) object, and reusing the identifier. So reusing 'string' as an identifier doesn't mean that any object has actually changed. > The only way I've found to accomplish this is the following: > > string = 'a string' > copy = string > string.replace 'a different string' # both string and copy are changed > > But this seems a very poor alternative, because it forces code operating > on the _original string_ to know that a _copy_ of the string exists! > (Heaven help us if string should be some arbitrary object instead of an > actual String!) This is clearly in violation of all sorts of good > programming principles. No it isn't. Ruby is designed along very sound lines; it just doesn't happen to be the same language design as C and others. Variables are bound to object references, and all references to a given object have exactly the same relation to the object as all other references. You have to know the difference between assignment and object mutation, and use whichever you think is right in a given situation. > Consider the following example, which could be a very realistic > usage in a Ruby address book. (Note: Person is an actual class > I've implemented that is extensively self-modifying, so the code > below is entirely possible. Basically, if the @foo instance > variable does not exist, Person.foo= redirects to first create a new > @foo instance variable and accessors for it, add it to @field_list, > and assign it. Assume that Person class is a library being used > here.) It sounds a lot like OpenStruct. > # code snippet 1 > p = Person.new # => # :last_name, > # :middle_name, :location, :primary_email, > # :primary_phone_number]> > p.personal_email = 'me@home.com' > p.primary_email = p.personal_email # I want p.primary_email to always > reflect > # the contents of p.personal_email > # p.inspect now produces # # @primary_email="me@home.com", @field_list=[:first_name, :last_name, > # :middle_name, :location, :primary_email, :primary_phone_number, > # :personal_email]> > > # code snippet 2, perhaps called by some library, who knows where > p.personal_email = 'me@gmail.com' # whoops, p.primary_email is no > longer the > # same as p.personal_email > Code snippet 2, being agnostic of the fact that we want to make a copy > of p.personal_email that will reflect any reassignment of it, has > unwittingly made it impossible for us to do so. Of course we could fix > that, nominally, by changing the implementation of Person.foo=: > > class << Person > def foo=(new_val) > @foo.replace new_val > end > end > > But changing libraries isn't a great idea, and it still violates the > principles of OO coding, because we've forced the library to reflect the > way we're using it. > > Worse yet, what happens when either @foo or new_val > is an object of a class other than String? Most likely a NameError, and > as far as I can tell, no way to get around it. > > Now, the most elegant solution I can think of would require some pretty > low-level meddling with the core of Ruby, and that would be to add some > level support for pointers, but that seems to be frowned upon by > Rubyists. > > The only other solution I can think of is to change the functionality of > Object.= to act like String.replace (not creating a new copy), requiring > coders to always use dup if they want to specifically specify creating a > new copy. I'm pretty sure we'd all agree that would be a terrible > solution. > > I've read a lot of "I don't think there's a realistic situation where > that functionality would be needed" type of responses in past > discussions of this, but I think the example I've described is pretty > common and realistic. If there's a better, elegant way to allow this > sort of functionality, please let me know. The things that come to mind are aliasing methods, overriding methods, and possibly writing a modified version of OpenStruct that automates the twinning of attributes, or maybe attr_twins or something. It's miles from anything that suggests to me a need to re-engineer Ruby's object model. It sounds like you're seeing a conflict between wanting a program to work a certain way, and writing it to work that way. I don't think there's a conflict. If you feel like you're tailoring a library too much to a single use case, that's still better than tailoring Ruby to a single use case :-) More to the point, if you take a step back and write something that handles this kind of use case, you can turn it around so that it's beneficial and comfortable and doesn't feel like a squeaky wheel. David -- Rails training from David A. Black and Ruby Power and Light: Intro to Ruby on Rails January 12-15 Fort Lauderdale, FL Advancing with Rails January 19-22 Fort Lauderdale, FL * * Co-taught with Patrick Ewing! See http://www.rubypal.com for details and updates!