From: "David A. Black" Date: 2007-10-19T20:09:03+09:00 Subject: Re: Class instance variable idiom Hi -- On Fri, 19 Oct 2007, ara.t.howard wrote: > > 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 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). >> 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, 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. > 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 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 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. >> 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. 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 :-) > 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. "Clearly evil" works for me :-) >> 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 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. > 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. (I'm happier with an equal-sign but I won't press the point :-) > 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 > [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. > 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. 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. David -- Upcoming training from Ruby Power and Light, LLC: * Intro to Ruby on Rails, Edison, NJ, October 23-26 * Advancing with Rails, Edison, NJ, November 6-9 Both taught by David A. Black. See http://www.rubypal.com for more info!