Issue #6688 has been updated by prijutme4ty (Ilya Vorontsov).


trans (Thomas Sawyer) wrote:
> =begin
> I don't think #replace works on Enumerables, rather it works on Array and Hash.
> 
> It does seem a rather powerful notion to be able to replace an object with another wherever is may be referenced. Though @shudo may be right that it is too dangerous. With good design, a proper interface can handle the reassignment through the usual mechanisms, although admittedly it may entail many more compute cycles to do it.
> 
> On the other hand, if Smalltalk supports it then perhaps it's worth consideration. Always respect the Smalltalk :) But I agree with it, better name is #become.  
> 
> Also note that a less dangerous notion of a generic replace is simply to copy common instance variables.
> 
>   class X
>     def initialize(a)
>       @a = a
>     end
>   end
> 
>   class Y
>     def initialize(a,b)
>       @a, @b = a, b
>     end
>   end
> 
>   x = X.new(1)
>   y = Y.new(2,3)
> 
>   x.a  #=> 1
>   x.replace(y)
>   x.a  #=> 2
> 
> Although a more appropriate name for this is probably #instance_replace. This preserves object identity, but can only be used to "copy" an object of the same type --which, if you think about it, is what Array#replace and Hash#replace does too actually.
> =end

Object copying is useful but far less powerful. Imagine that you want to redefine files ARGF is linked to. Instead of
ARGV = ['filename','filename2']; puts ARGF
you should write
ARGV.replace ['filename','filename2']; puts ARGF

But now imagine that you want to use lazy enumerator to print content of all files in a directory. You don't want to create an array of all files, so you just try to write 
`ARGV.replace Dir.to_enum(:glob,'**/*.txt'); puts ARGF`
...and fails because you can't use enumerable here, only an array. Why we should put up with it if it can be improved not to break duck-typing paradigm?
----------------------------------------
Feature #6688: Object#replace
https://bugs.ruby-lang.org/issues/6688#change-27955

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


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/