On Oct 19, 2007, at 5:09 AM, David A. Black wrote: > > I think we're defining encapsulation differently. I don't mean that no > two objects should ever share any kind of data; rather, it's that > class variables don't play nicely with the system of which they are > part. I know about the "singleton" non-singletons... but at least > that's implemented in terms of inheritance (so really only the > terminology is at stake). I can see what a subclass has to do with its > parent class, especially given Matz's point that inheritance in Ruby > is mostly about "shared implementation". I find automatic variable > sharing between classes and instances much more problematic, > especially given the fact that classes are first-class objects (which > leads to all the problems with @@vars obscuring @vars, conceptually at > least). yeah i see that. i avoid them much of the time for those reasons. but, technically, doing something like this class C SHARED_WITH_ALL_SUBCLASSES_AND_INSTANCES = [] end does the same. it's something worth considering. >> and constants and singleton methods.... of course class singleton >> methods, > > It's not a contest, though :-) It's possible that constants are > well-designed and class variables aren't, even if they share certain > behaviors. Actually, I'd add constants to the list of things, along > with instance variables, that can do most of what people usually use > class variables for. > sort of. if you need state to be inherited you can use constants. if you want true class singleton state that is settable you can use class instance variables. if you want state that is settable AND inherited the simplest option at the moment are class @@variables. the way they mix into instances, i would agree is confusing, but there really aren't any other *simple* options for inheriting state in classes >> in an instance context, require a 'self.class' prefix to invoke >> while constants do not but this is not encapsulation - it's the >> breaking of it - instances *should* be allowed to access the >> unfettered methods of their class because requiring the >> 'self.class' prefix also makes them *public* which, of course, >> seriously breaks encapsulation. > > I'm not following this, I'm afraid. If an object has a public method > defined on it, and you call obj.meth, how does that break > encapsulation? I guess it's the "*should*" I'm not understanding. in some languages instances of a class may call private/protected methods of that class which, if you think about it, makes sense. in ruby, a method must (ignoring xxx_eval and send) be exposed to the world if instances also need to call it. so when using class instance vars you give instances one simple option for accessing them: expose them through attr_accessor thereby also exposing them to the world. for example class C @how_do_instances_get_at_this def initialize class << self.class def with_a_public_accessor() @how_do_instances_get_at_this end end self.class.with_a_public_accessor end so the typical pattern with class instance variables is to expose them publicly and this may break encapsulation iff the information exposed is best left internal to the class and it's instances > > OK... but I don't think we have to choose between (a) no two objects > ever sharing anything, and (b) deciding that some objects share some > things so we might as well give up trying to differentiate among > different cases. It's possible that mixins are well-designed and class > variables aren't, even if both of them involve, described at a very > high level, tearing down barriers between bits of code. > > The sense I get from class variables is of quasi-globalness. I don't > get that sense when I include a mixin; I actually don't think it's the > same in either kind or degree. > yeah the fact that instances see them does indeed make them seem quasi global - but the lack of another inherited class state mechanism is a show stopper when it comes to declaring them useless and poorly designed. that's critical functionality in some object hierarchies. > > I'm not sure what the advantage is of generalizing the technical term > "instance" that way. You can say that global variables are associated > with an "instance" of a computer program, so they must be OK :-) > heh. i actually meant it quite specifically with regard to classes - each iteration of oo laguanges is breaking down the barrier between classes, objects, and other constructs like modules. i think we all can see that the distinction in ruby is largely artificial: layers put on top to make us see them as distinct. i probably program a lot more class factories than your average ruby programmer and, when you are working with classes at that level you, correctly, are thinking of them as instances. but you also start wondering why things like this don't work: class C @var = 42 class << self p @var end end and this too class B < C p @var end if you've programmed using prototypes this shouldn't really come as a surprise: it's normal to expect classes that *are* objects to have some mechanism for sharing or copying that object's state. in ruby you have to resort to @@vars and other weirdness like klass.clone or klass.dup to get the affect. in summary it's correct, i think, in a language where classes are first class objects to consider attributes of that class, like it's singleton class and it's child classes, as *instance* data. >> >> class Parser >> >> def self.bufsize(*value) >> value.size == 0 ? @@bufsize : self.bufsize=(value.first) >> end >> >> def self.bufsize=(value) >> @@bufsize = Integer value >> end >> >> bufsize 4242 >> >> def initialize options = {} >> @bufsize = options[:bufsize] || @@bufsize > > I don't think it would be such a disaster to have to say "|| > self.class.bufsize". It would certainly be worthwhile tradeoff for > all the other problems. i disagree - if you do that you've designed a class that cannot be subclassed: as that line will return 'nil' in those subclasses. if you do that you should be using a singleton object or module - not a class - to model your problem domain. >> >> i can hear people thinking "you need instance variables!" sure, >> if you want to write totally non-reusable code. why? because if >> i do >> >> class Parser >> def self.bufsize(*value) >> value.size == 0 ? @bufsize : self.bufsize=(value.first) >> end >> > [code snipped] >> >> in short there are a million reasons to use class variables >> revolving around the glitch that class inheritance in ruby does >> not provide a straightforward way for child classes to have any >> sort of setup step performed and that class variables let you >> perform that setup once in a way that is inherited and yet >> settable by client code. > > I'm still happier with class objects playing in the same ballpark as > other objects, when it comes to per-object state. Set up, if desired, > a way for other objects to get at the state, and then have the other > objects send you messages. It's all about where one thinks the > special-case horizon should be, I guess. the lack of class setup with class instance variables isn't really a special case though - any newbie would expect to be able to write this class C @a, @b = 4, 2 end class B < C end and have @a and @b end up being initialized in B somehow. i guess i just don't think that inheritance/setup of class state is something that people should be expected to roll on their own in a language as powerful as ruby - it can get quite complex when modules enter the picture too. i'd rather entrust matz to solved the problem generically ;-) > > I'm not saying they should. I'm saying I'd like it if class variables > did :-) It's not a one-size-fits-all argument about misunderstood Ruby > features; it's specifically about class variables. > > oh i understand that david - no worries. what i'm saying is simply that i've probably designed as many complex ruby class hierarchies as anyone else out there and i've solved the class state sharing issue a whole bunch of times in different ways: sometimes using 'inherited', sometimes by factoring things out into modules, and sometimes using @@variables and none of them are perfect. my experience has lead me to think that any discussion of the evils of class variables (and i've said they are evil on this list too!) shouldn't be outside the context of how to *better* share and initialize state between parent and child classes because it's an important feature in an oop language - in short, it's a red herring. cheers. a @ http://codeforpeople.com/ -- share your knowledge. it's a way to achieve immortality. h.h. the 14th dalai lama