From: Mark Hubbart Date: 2005-01-20T03:51:37+09:00 Subject: Re: value by reference On Thu, 20 Jan 2005 00:11:40 +0900, Michel Martens wrote: > It's easy to accomplish this task with strings: > > def do_something(a, b, c) > a.replace((a.to_i + 1).to_s) > b.replace((b.to_i + 2).to_s) > c.replace((c.to_i + 3).to_s) > end > > a, b, c = '5', '6', '7' > do_something(a, b, c) > > It would be nice to have a replace method for every object, then we could write: > > def do_something(a, b, c) > a.replace(a + 1) > b.replace(b + 2) > c.replace(c + 3) > end > > a, b, c = 5, 6, 7 > do_something(a, b, c) String#replace doesn't change the object the variable refers to, it just modifies the contents of the string; the internal state of the same object. This can't be done with integers and symbols, for example, because they are immutable. And crossing types would be impossible. So, you really don't want String#replace for everything else, you want something new. There have already been several good explanations of why variables don't act this way in ruby, so I'll skip that. I'll just say while ruby's method is diferent from any other language I've worked with, it is not inconsistant, and once I figured out the model, I found other ways to do things than via "pass by reference". I still end up wanting to do things in the caller's scope (binding) occasionally, but that's not the same issue. That said, just about anything is possible in Ruby, so here you have it: a Ref class. class Ref instance_methods.each{|m| undef_method(m)} def self.new(obj) ref = allocate ref.become obj ref end def method_missing(*args, &block) @ref.__send__(*args, &block) end def become(obj) old = @ref @ref = obj old end def __value__() @ref end end when you call Ref.new(obj), you get back a Ref object, which will appear in every way to be the "obj" that you passed to Ref.new. It will give the same id, behave the same, respond to the same methods, etc. Only two differences (that I see): First, it responds to two extra methods: __value__ and become. become replaces the object that is being referenced, and returns the old value. __value__ returns the object being referenced, without the Ref wrapper. You should use this method any time you want to serialize or Marshal the value. Second, it circumvents the public/private method checks, so you can call private methods directly on the reference. Sorry. Don't have the time to figure out how to fix that without eval. Examples: def do_something(a,b,c) a.become a+1 b.become b+2 c.become c+3 end ==>nil a,b,c = Ref.new(2), Ref.new(4), Ref.new(6) ==>[2, 4, 6] do_something(a,b,c) ==>6 [a,b,c] ==>[3, 6, 9] do_something(a,b,c) ==>9 [a,b,c] ==>[4, 8, 12] a = Ref.new(12) ==>12 a.id ==>25 a.become a.to_f ==>12 a ==>12.0 a.id ==>2938980 a.become a.to_s ==>12.0 a ==>"12.0" a.id ==>2927800 In closing, while this was a fun exercise to code, I doubt I'll ever use it :) It's the wrong hammer for my particular nails. cheers, Mark