Issue #6688 has been updated by trans (Thomas Sawyer).


=begin
I have idea for less dangerous form of become. 

I am working with a parser that supports object references. It is difficult b/c children of a given object might reference the parent, creating circular structures. So you can't just parse the children without first adding the parent to an alias table in case a child references it, but you also can't create the parent without the children. So the first thought is to allocate the parent, put the allocation in the alias table and then comeback and add the children tp the parent afterwards. Unfortunately not all classes support allocate, nor do I like the idea of depending on the mutability of the allocated object. So I thought maybe using a Proxy for the object while the children are parsed would work, and it almost does, except now I must replace all the proxies with the original object. But guess what? While I have a reference to the proxy, there is no one way to traverse the all children of an arbitrary object. I can't say where the proxy ended up. 

So that problem led me the idea. Instead of any object supporting #become, only an instance of a Proxy class could do so. e.g.

    p = Proxy.new
    a = p
    p.become(1)
    a #=> 1

Allowing only this one type of object to do this would limit the dangers to controlled usage, while providing a very powerful technique when tricky situations like the one I described above arise. I guess this Proxy class is like the concept of a Future, but one that's supported more deeply in the language via #become.
=end

----------------------------------------
Feature #6688: Object#replace
https://bugs.ruby-lang.org/issues/6688#change-36674

Author: prijutme4ty (Ilya Vorontsov)
Status: Open
Priority: Normal
Assignee: 
Category: core
Target version: Next Major


I suggest that #replace works not only on Enumerables but on any Object. It can make use the same object in different places more consistent. It makes it possible to write
class Egg; end
class Hen; end
class HenHouse; attr_accessor :species; end
class Incubator; def incubate(egg) Hen.new; end

# Here it is!
class IncubatorWithReplace; 
  def incubate(egg) 
    egg.replace(Hen.new)
  end
end

e1,e2,e3 = Egg.new, Egg.new, Egg.new
h1, h2 = HenHouse.new, HenHouse.new

# One egg is shared between hen houses
h1.species = [e1, e2]
h2.species = [e1, e3]
p h1 # ==> <HenHouse @species = [#<Egg 1>,#<Egg 2>]
p h2 # ==> <HenHouse @species = [#<Egg 1>,#<Egg 3>]


 # First option. It's bad choise because it makes two "data structures" HenHouse inconsistent: 
 #   they have different object while must have the same
h1[0] = Incubator.new.incubate(h1[0])
p h1 # ==> <HenHouse @species = [#<Hen>,#<Egg 2>]
p h2 # ==> <HenHouse @species = [#<Egg 1>,#<Egg 3>]

 # Second option is ok - now both shared objects're changed.
IncubatorWithReplace.new.incubate(h1[0])
h1 # ==> <HenHouse @species = [#<Hen>,#<Egg 2>]
h2 # ==> <HenHouse @species = [#<Hen>,#<Egg 3>]  

 # Third option is bad - it wouldn't affect HenHouses at all
e1 = Incubator.new.incubate(e1)
p h1 # ==> <HenHouse @species = [#<Egg 1>,#<Egg 2>]
p h2 # ==> <HenHouse @species = [#<Egg 1>,#<Egg 3>]

 # while Fourth option is ok and works as second do
IncubatorWithReplace.new.incubate(e1) ## would affect both HenHouses
p h1 # ==> <HenHouse @species = [#<Hen>,#<Egg 2>]
p h2 # ==> <HenHouse @species = [#<Egg 1>,#<Egg 3>]


I can't imagine how it'd be realized, it looks like some dark magic with ObjectSpace needed to replace one object at a reference with another at the same reference. But I didn't found a solution.

About ret-value. I think it should be two forms:
Object#replace(obj, retain = false)
If retain is false #replace should return a reference to a new object (in fact the same reference as to old object but with other content)
If retain is true, old object should be moved at another place and new reference to it is returned, so:
e1 # ==> <Egg id:1>
e1.replace( Hen.new, true ) # ==> <Egg id:2>
e1 # ==> <Hen id:1>


-- 
http://bugs.ruby-lang.org/