From: itsme213 Date: 2004-12-11T11:17:25+09:00 Subject: Re: Freezing Variable Assignment "David A. Black" > > Your rand() example is about (4). > > rand() has to mutate some stored state somewhere (i.e. some variable, > > instance, global, class, C-only, ... ). Your obj can reach and modify that > > stored state via rand(). I am guessing obj.rand[10] would affect the outcome > > of a subsequent obj2.rand[10] i.e. this random number state is cetainly not > > local stored state (1) in obj. > > I don't see how or where any state is stored. Are you talking about > the behavior of the random number generator, and the fact that *it* > isn't in the same state after calling obj.[]? If so, I definitely do > not consider that part of the state of obj itself. And that would be quite reasonable. But my build up to that is: what you consider virtual object state is specific to your intent, and is derived ultimately from slots, as follows: (a) obj has no local stored slots that reflect state of the rng (that it has no local stored slots at all is coincidental) (b) If you transitively walk all slots from obj, any subset of that space _could_ be what you consider virtual state of obj. (c) what you consider _relevant_ virtual object state is defined by some very specific transitive traversal of slots (via read-accessors). I'll use #freeze to illustrate not because it is widespread, but because it gives a vivid example of the boundary of relavant object state. So a good check would be how you would define a custom #freeze e.g. If I implemented Car with @wheel1...@wheel4, I might just use the default Object#freeze. If I implemented Car with @wheels=[], I might want def freeze; super; @wheels.freeze; end A bit more involved example: class A attr_accessor :a, :b, :c def initialize; @a=1; @b = []; @c = [C.new, C.new]; end def freeze; super; @b.freeze; @c.freeze; @c[0].freeze; end # note: a very custom freeze boundary going out to @c[0] # note: it would not make sense to freeze @c[0] without freezing @c # that would allow swapping out @c[0], but leaving the original @c[0] frozen # so virtual state should be some transitive traversal of slots # House#freeze should not freeze the door knob without freezing the door # note also that (the virtual state of) @c[1] is not frozen. end My defintion of #freeze should be aligned with my class-specific notion of the state of my objects. I visualize this freeze literally as how far out you want the freeze to propagate, moving outward along slots starting from obj. Similar thinking goes into my corresponding definitions of hash, deep copy, equality checks for unit tests, etc. (d) There is rng state somewhere, and rand() itself somehow accesses and manipulates that state. Where that state is, and whether or not you want to consider it part of the virtual state of obj is entirely specific to the desired semantics for your obj. It is reasonable for you to not consider the rng as part of obj state. On the other hand, I can also conceive of an implementation of rand() that installs an @rng slot on obj itself, points it at a new RandomNumberGenerator instance (with its own slots representing the state of that generator). And I can conceive of situations I would want obj#freeze to totally freeze the state of that RNG instance as well (or not). > but using [] and an argument. So calling obj.[] is just a wrapper > around rand. An integer is returned, but it isn't saved anywhere. Right. But that integer is derived from some non-local rng state that is accessible from rand(). And in your case you chose that your semantics of obj should not include that state as part of its virtual state e.g. of the state you would freeze. > > x = X.new > > x.compute #=> 5 > > x.y.z.mutate 6 > > x.compute #=> 11 > > x.freeze > > x.y.z.mutate 7 #=> error: can't modify frozen object > > # likewise x == x1, x.hash(), someHash[x], etc. > > > > To even mentally understand what is going on, I (and most others, I suspect) > > need some conceptual model of state that covers (1), (2), (3), and (4). At > > least a mental model; though one in the code could aid testing. Some uniform > > notion of "slot" is helpful for this. > > I'm afraid I don't mentally understand what's going on in your example > :-) How are #y and #z defined? Assume x.y.z traverses out two objects from x. Just like my freeze example in (b), I might want x.freeze to propagate out because I happen to consider that Xs state must include the z object (Furthermore, I might want to include just some of the state of the z object that are modified by Z#mutate .... but that kind of slot-specific freeze is not allowed by Ruby) > "Contains" in the sense that Array#[] shows you what's in the > collection, as does Hash#[]. [] doesn't always index a collection, > even in built-in classes, and certainly doesn't have to; but my point > was that my object acts as if it were a typical collection, using [] > to get at elements. So would you use "contains" for anything a method might return? Or would you limit it to some subset of those things it's slots can get to, directly or indirectly? I think I read more into it than you meant. Sure, [] (or any accessor) can return any stored or derived value, from colletions or otherwise. Some of them may be part of what you choose to consider your object state, others not. Did that make it any better? Or worse? :-)