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