"Pointer" to an object? (i.e. reflect changes to original)

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

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. 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.)

  # code snippet 1
  p = Person.new # => #<Person:{id} @field_list=[:first_name,
: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 #<Person:{id} @personal_email="me@home.com",
  # @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.

Thanks,
Doug

···

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

Hi --

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 # => #<Person:{id} @field_list=[:first_name,
: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 #<Person:{id} @personal_email="me@home.com",
# @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 :slight_smile: 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

···

On Mon, 8 Sep 2008, Doug Glidden wrote:

--
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!

def p.primary_email
    personal_email
  end

Or closer to the pointer solution you want:

  p.primary_email = lambda { p.personal_email }
  p.personal_email = 'me@gmail.com'
  puts p.primary_email.call

The lambda expression corresponds to the address operator, the .call
corresponds to a pointer dereference.

Peter

···

On Mon, Sep 8, 2008 at 4:39 PM, Doug Glidden <41mortimer@gmail.com> wrote:

# code snippet 1
p = Person.new # => #<Person:{id} @field_list=[:first_name,
:last_name,
                # :middle_name, :location, :primary_email,
                # :primary_phone_number]>
p.personal_email = 'me@home.com'

David A. Black wrote:
[snip]

You have to know the difference between assignment and object
mutation, and use whichever you think is right in a given situation.

Okay, but...well, I'll comment more on this in a minute.

[snip]

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.

I don't think I'm suggesting re-engineering the object model, merely
adding the possibility of creating pointers. It could be (and probably
is) as simple as adding a Pointer class, but as far as I can tell that
would require delving into C, which is definitely not my forte.

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 :slight_smile:

I don't think it would be doing that. I thought Ruby was about
freedom--it lets you make modifications to pretty much anything you
want, for goodness' sake--but this seems like a pretty severe
restriction to me. I don't see how wanting one object to be able to
reflect changes to another is such a specialized or rare 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

The long and short is, I don't know how to do this. Maybe I'm being
dense, but I'm not catching what you have in mind with the list of
options above (aliasing methods, overriding methods, etc.). The most
elegant solution I've come up with is an ObjectWrapper class...

  class ObjectWrapper
    attr_accessor :wrapped_object

    def initialize(object)
      @wrapped_object = object
    end

    def method_missing(method, *args)
      @wrapped_object.send method, *args
    end

    def respond_to?(method)
      @wrapped_object.respond_to? method
    end
  end

...which still seems decidedly inelegant (not to mention extraordinarily
wasteful), as it requires wrapping every object that could ever be
pointed to with it. IMHO, an object shouldn't need to know that it's
being pointed to. In other words, the following is elegant and
least-surprising, but impossible to implement without dropping into C,
as far as I can tell:

  object = MyObject.new
  copy = *object
  ...
  object = MyOtherObject.new # object and copy are both changed

On the other hand, forcing the following is neither elegant nor
least-surprising:

  object = ObjectWrapper.new MyObject.new
  copy = object
  ...
  # I might accidentally use object = MyOtherObject.new -- whoops! That
doesn't
  # do what I expected at all, but its not a noticeable error, either.
  object.wrapped_object = MyOtherObject.new # this does what I intended

Furthermore, anywhere in the program that we want to access the MyObject
instance itself, we have to reference object.wrapped_object, which is
painful and far too easy to forget.

If Object had a #replace method like String, Hash, Array, et al, that
would be an acceptable (albeit annoying) workaround, but that's not
available either.

I'm wide open to better ideas.

Doug

···

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

Calamitas wrote:
[snip]

Or closer to the pointer solution you want:

  p.primary_email = lambda { p.personal_email }
  p.personal_email = 'me@gmail.com'
  puts p.primary_email.call

The lambda expression corresponds to the address operator, the .call
corresponds to a pointer dereference.

Peter

Peter,

Thanks! I had just (literally within the last ten minutes) thought of
the possibility of using an enclosure of some sort to do the trick. I
was playing around with using a proc, you used a lambda, I think either
one will probably work with some tweaking. This is the "perfect"
solution, I think.

I'm a little curious how this will react if p.primary_email.call is
executed when p is not in scope, e.g. if something like the following
occurs, so I'm going to play around:

  def do_something(person)
    email = person.primary_email.call
    # do more stuff...
  end

  p = Person.new
  p.personal_email = 'me@home.com'
  p.primary_email = lambda {p.personal_email}
  do_something p

Will the lambda be able to handle that situation? Like I said, I'm
going to play around with it.

Thanks again,
Doug

···

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

Hi --

David A. Black wrote:
[snip]

You have to know the difference between assignment and object
mutation, and use whichever you think is right in a given situation.

Okay, but...well, I'll comment more on this in a minute.

[snip]

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.

I don't think I'm suggesting re-engineering the object model, merely
adding the possibility of creating pointers. It could be (and probably
is) as simple as adding a Pointer class, but as far as I can tell that
would require delving into C, which is definitely not my forte.

It's a radical change because it adds a completely new axis to the
language, in which variables have a significance that's quite
different from what they have now. Ruby variables don't know about
each other; if you do:

   a = x
   b = x
   c = x

x doesn't know how many references there are to it, and reusing any of
the three identifiers has no implications for the others. If you have:

   a = x
   b = *a
   a = y
   # b is now y

(aside from the fact that *a is already meaningful, but never mind
that) you've now introduced the concept of a pointer to a variable,
which is very different from a reference to an object.

The long and short is, I don't know how to do this. Maybe I'm being
dense, but I'm not catching what you have in mind with the list of
options above (aliasing methods, overriding methods, etc.). The most
elegant solution I've come up with is an ObjectWrapper class...

class ObjectWrapper
   attr_accessor :wrapped_object

   def initialize(object)
     @wrapped_object = object
   end

   def method_missing(method, *args)
     @wrapped_object.send method, *args
   end

   def respond_to?(method)
     @wrapped_object.respond_to? method
   end
end

...which still seems decidedly inelegant (not to mention extraordinarily
wasteful), as it requires wrapping every object that could ever be
pointed to with it. IMHO, an object shouldn't need to know that it's
being pointed to. In other words, the following is elegant and
least-surprising, but impossible to implement without dropping into C,
as far as I can tell:

object = MyObject.new
copy = *object
...
object = MyOtherObject.new # object and copy are both changed

On the other hand, forcing the following is neither elegant nor
least-surprising:

object = ObjectWrapper.new MyObject.new
copy = object
...
# I might accidentally use object = MyOtherObject.new -- whoops! That
doesn't
# do what I expected at all, but its not a noticeable error, either.
object.wrapped_object = MyOtherObject.new # this does what I intended

Furthermore, anywhere in the program that we want to access the MyObject
instance itself, we have to reference object.wrapped_object, which is
painful and far too easy to forget.

If Object had a #replace method like String, Hash, Array, et al, that
would be an acceptable (albeit annoying) workaround, but that's not
available either.

There's talk now and then of a "become" method, which would act kind
of like what you're describing:

   a = something
   c = a
   a.become(another) # all something refs are now another refs

but it might actually do too much. (There's an implementation of it in
the 'evil.rb' library, I believe.)

I would consider something like this, which achieves the kind of
coupling you need in a fairly specific way (it doesn't affect all
references, but affects the way the attributes relate):

   module Twinnable
     def attr_twin(a,b)
       (class << self;self;end).class_eval do
         attr_accessor b
         define_method(a) { send(b) }
         define_method("#{a}=") do |val|
           send("#{b}=", val)
           instance_variable_set("@#{a}", val)
           instance_variable_set("@#{b}", val)
         end
       end
     end
   end

   class Person
   end

   p = Person.new
   p.extend(Twinnable)

   p.attr_twin(:personal_email, :primary_email)

   p.personal_email = "abc"
   puts p.primary_email # abc

   p.personal_email = "def"
   puts p.primary_email # def

Or you could generalize this into an OpenStruct-like class, and have
Person descend from it.

David

···

On Tue, 9 Sep 2008, Doug Glidden wrote:

--
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!

Doug Glidden wrote:
[snip]

Will the lambda be able to handle that situation? Like I said, I'm
going to play around with it.

Thanks again,
Doug

Impressive. It works exactly as it should. Both lambda and proc work
as well, incidentally. Actually, from what I've read they're identical
in 1.8 (which is what I'm using), but not in 1.9, so it may not work the
same in Ruby 1.9. To be safe, I'll use the lambda.

Thanks again, Peter.

Doug

···

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

Hi --

···

On Tue, 9 Sep 2008, Doug Glidden wrote:

Calamitas wrote:
[snip]

Or closer to the pointer solution you want:

  p.primary_email = lambda { p.personal_email }
  p.personal_email = 'me@gmail.com'
  puts p.primary_email.call

The lambda expression corresponds to the address operator, the .call
corresponds to a pointer dereference.

Peter

Peter,

Thanks! I had just (literally within the last ten minutes) thought of
the possibility of using an enclosure of some sort to do the trick. I
was playing around with using a proc, you used a lambda, I think either
one will probably work with some tweaking. This is the "perfect"
solution, I think.

I'm a little curious how this will react if p.primary_email.call is
executed when p is not in scope, e.g. if something like the following
occurs, so I'm going to play around:

def do_something(person)
   email = person.primary_email.call
   # do more stuff...
end

p = Person.new
p.personal_email = 'me@home.com'
p.primary_email = lambda {p.personal_email}
do_something p

Will the lambda be able to handle that situation? Like I said, I'm
going to play around with it.

Yes; the lambda carries its local scope with it, so wherever it's
called, it will have access to the p that it started with.

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!