From: itsme213 Date: 2004-12-12T05:17:23+09:00 Subject: Re: Freezing Variable Assignment "David A. Black" wrote > I think where I came in was discussing the freezing of local variable > bindings, which I didn't (and don't) like because it's such a > different kind of freezing from freezing a specific object (as done by > Kernel#freeze). Which you disagreed with, etc. :-) Leaving aside freezing local variables, I am curious what you think of freezing controlled at the level of (the bindings of) individual Ruby instance variables, rather than strictly for entire objects. So I can say things like: class Home; def freeze ... end; end myHome.freeze # cannot change door or door-knob, but can re-paint door myHome.door = otherDoor #=> error myHome.door.knob = otherKnob #=> error myHome.door.color = otherColor #=> OK Today's Object#freeze can be implemented using this, I believe (extended to deal with special cases for arrays, hashes, etc, and with the fact that "freeze all instance variables, ... " in Ruby might mean prohibit dynamic addition of new ivars). The converse is not true i.e. instance-variable level freeze cannot be implemented using today's Object#freeze, and the myHome example above is impossible to implement. Further, since a class can be re-opened and extended, including introducing new instance variables, should a custom #freeze really be forced to have a scope across all possible extensions? > I certainly don't see any reason not to define custom freeze's for > specific objects, though there might be certain things you'd want to > watch out for. One problem with the "deep freeze" might be that while > objects cannot share instance variables, they can share state by > having i.vars that refer to the same object. So if it's in the > interest of one to freeze that deep object (as opposed to just > freezing the binding), but not in the interest of another, I guess > weird things could happen. True. Note, though, that this is not specific to freeze. In general, such aliasing of objects (anytime the object graph is not a tree, which is most of the time) is an issue for all mutative methods, including freeze. > I'm thinking of something like (starting with non-weird case): > > class A > attr_accessor :x > def x_add(str) > @x << str > end > end > > class B > attr_accessor :y > end > > a = A.new > b = B.new > > shared = "hi" > > a.x = shared > b.y = shared > > b.freeze # freezes @y binding, but doesn't freeze @y > > a.x_add(" there") # so this works > > puts b.y # hi there > > Now if you deep-froze b in such a way that @y (the object itself) was > frozen, then a would be in for a surprise when it called x_add. > (That's the "weird" case :-) It's probably a fairly marginal > scenario, but it's definitely not impossible. Absolutely correct. But either a and b are meant to work with shared state (in which case a.x_add, b.y, and freeze have no surprises), or they are not (in which case b.y and a.x_add themselves are anomolies, even without #freeze). This is the reason why there are design approaches and even language features meant to support encapsulating such aliasing within manageable boundaries while still permitting arbitrary object graph structures within them. Such things might not be in Ruby, but that does not make them less worth considering. Cheers.