From: "ara.t.howard" Date: 2007-10-19T14:06:03+09:00 Subject: Re: Class instance variable idiom On Oct 18, 2007, at 11:22 AM, David A. Black wrote: and ara responds to him but mostly to the general thread: > > I'm on record as saying that class variables are the thing I dislike > most about Ruby, so I'll try to explain why. > > They break encapsulation. i honestly don't think they do - inheritance breaks encapsulation. for example class A CONST = 42 class << self def shared() 42.0 end end end class B < A end p B::CONST p B::shared class vars are exactly like this - only with state that happens to be resettable. i don't see anything particular about the way class vars behave that is different from constants or singleton (note the *single* in singleton methods that can be called on multiple objects!). it's the concept of inheritance that breaks encapsulation, this is of course generally accepted by oop geeks, and ruby doesn't change this wart on oop's many graces. > They are visible to a weird assortment of > objects: class C, instances of C, class D < C, instances of D... all > the same variable. This cross-section of objects doesn't have anything > in common except that they can all see each other's class variables. > and constants and singleton methods.... of course class singleton methods, 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. in point of fact inheritance is *designed* to break encapsulation so that we can reuse code and remain sane. child classes, mixins, friend classes et al are all about tearing down the barriers between bits of code so we don't descend.into.dot.dot.dot.dot.hell and develop carpal tunnel from all that code pasting > The prefix @@ makes it appear that they have some connection or > kinship with instance variables. Actually, they're almost the > opposite. Instance variables represent state on a strictly per-object > basis. Class variables cut across many different objects and scopes. > I would prefer them to look like $$this, since they're really a > kind of restricted global variable rather than an extended instance > variable. i can see that. still, they *are* associated with an instance - an instance of a class hierarchy. i realize people don't generally seem to grasp this concept though. the idea of switching syntax is a good one. i hate $ because it looks like perl and perl is clearly evil. i'm not sure what would be better though. > > The worst thing about them, in my view, is the amount of confusion > they have caused. The fact that there is something called a "class > variable" -- and that it looks vaguely like an instance variable -- > has been a huge obstacle for people trying to get a grasp on the idea > that classes are objects and can, via *instance* variable, have their > own state. Class variables throw a big shadow over that otherwise very > consistent and lucid state of things. I've seen this happen again and > again for seven years. absolutely correct. > > In Ruby > 1.8, class variables are going to be somewhat more strictly > per-class. That's a mixed blessing. They're going to come closer to > representing a class's state, but then the question will arise: why > have both that *and* instance variables of classes? It's not > impossible to answer that, but it's not a bad question. > > That sums up my views. Just so you know: I love Ruby madly :-) This is > one of the very few areas where I dissent strongly from the way it's > designed. > yeah i see all that. still, i've deigned quite a few ruby classes and i can assure people that they will miss them when they are gone/ limited. the reason, curiously, is that classes do not have an initialize method/hook. let me explain: let's say you want to do something quite reasonable like so 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 end end Parser.new that is to say you want to grab default initialization options from your class and you want users to be able to configure the class such that it stamps out instances with those defaults. it's really nice if people can configure via the environment or like so Parser.bufsize 42 as i'm sure we can all agree. obviously constants aren't really in order unless you want to use hackery like class Parser BUFSIZE = [ :value ] def self.bufsize=(value) BUFSIZE[0] = Integer value end ... blechh! 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 def self.bufsize=(value) @bufsize = Integer value end bufsize 4242 def initialize options = {} @bufsize = options[:bufsize] || self.class.bufsize end end Parser.new then, seemingly, all is well. but then class SubParser < Parser ... now i have a problem: *only* the Parser class has an initialized of the class instance variable @bufsize - it's of course 'nil' in SubParser. now, it's true that we can write something like class Parser def self.inherited other super if defined? super ensure other.init ### note that this must be *public* less we result to class_eval... end def other.init bufsize 4242 end init() this all starts to get just as confusing as the current situation. i can hear people now: "why is it so hard to inherit class variables in ruby?" 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. anyhow i really think it's the syntax, not the concept, that is cause for confusion. my 2cts. ps. there are a lot of powerful concepts in ruby that are mostly mis- understood: closures, mixins, singleton classes, callcc, throw/catch, and lambda abstraction are part of what allows ruby to grow in usefulness beyond the realm of the 'mere scripting language' some people set out to learn. i would be loathe so any of them vanish. pss. encapsulation is vastly overrated. when is that last time you used 'protected' in any ruby code? ;-) a @ http://codeforpeople.com/ -- it is not enough to be compassionate. you must act. h.h. the 14th dalai lama