Class instance variable idiom

hey rick - really good stuff. i learned somethings smalltalk too. i'll keep this brief because i'm running out the door: i've had at least two publicly released stabs and making reasonable class variable semantics. this simples is attributes (gem install attributes) which gives you

class C
   attribute('title'){ "the #{ name.upcase }" }
end

class D < C; end

p C.title #=> 'the C'
p D.title #=> 'the D'

in which declaring the attribute does not initialize it - rather the block is stored and later instance eval'd for a lazy initialization. this gives a kind of 'initialize' step that is useful in some situations but it's only on a per attribute basis and it lacks the notion of inheritance. for that i generally roll something custom like:

cfp:~ > cat a.rb
require 'attributes'

class C
   class << self
     attribute('a'){
       catch(:value){
         (ancestors - [self]).each do |ancestor|
           break unless self <= ancestor
           ancestor.module_eval{ throw :value, @a if defined? @a }
         end
         default = 42
       }
     }
   end
end

class D < C; end
class E < D; end
class F < E; end

p D.a

E.a = 42.0

p F.a

cfp:~ > ruby a.rb
42
42.0

which basically reads 'get your class variable from the first ancestor that has defined it'. i've called this 'inheritable_attribute' in some recent rails code but i'm not sure if it's a good name... i may add it to attributes but i've yet to decide if it's better to return '@a' or '@a.dup' - both have pros and cons...

i tackled this long ago with the traits lib too:

   http://codeforpeople.com/lib/ruby/traits/traits-0.9.2/README

but it's way too heavyweight for many purposes...

it's worth noting that both approaches create a different kind of inheritance that @@vars - which is far too unrefined for many use cases.

cheers.

a @ http://codeforpeople.com/

···

On Oct 19, 2007, at 10:22 AM, Rick DeNatale wrote:

An interesting side note. In Smalltalk, the declaration of variables
doesn't initialize them. Conventionally, class variables are
initialized in a class method called initialize. I'm a bit rusty on
this but IIRC this is something which has to be done manually after
defining the method and before instantiating any instances of the
class.

--
it is not enough to be compassionate. you must act.
h.h. the 14th dalai lama

>
> So what was my point here? Oh yes. I was talking about what happens
> when you make certain changes to the inheritance structure. If we
> had:
>
> class A
> end
>
> class B < A
> @@cv = 42
> end
>
> And then later
> class A
> @@cv = 57
> end
>
> Ruby currently at most warns about a conflict then goes ahead and adds
> a new class variable to A, leaving the existing one in C.

Ok, you've used a similar example multiple times now, so my curiosity
is getting the best of me - do you actually code this way, or are you
trying to come up with a contrived example to show the alleged
problems with class variables? Wouldn't you typically initialize the
class variable in the "superest" class only?

Imagine that this code is not all in one file, but is spread out
between the implementation and usage of a framework like, say, rails.

> ...
> On the other hand, there might be subtleties I can't yet fathom, much
> like I still don't understand why 1.9 changed the semantics of:
>
> module M
> def meth
> "M"
> end
> end
>
> class A
> include M
> end
>
> class B < A
> def meth
> "B"
> end
> end
>
> class C
> include M
> end
>
> C.new.meth
>
> So that in 1.9 this used to result in "M" but then they changed it
> back to the 1.8 semantics which ignore the re-inclusion and results in
> "B".

Should 'class C' be 'class C < B' ?

Yes, thanks!

···

On 10/19/07, Brian Adkins <lojicdotcom@gmail.com> wrote:

On Oct 19, 12:22 pm, "Rick DeNatale" <rick.denat...@gmail.com> wrote:
> On 10/19/07, ara.t.howard <ara.t.how...@gmail.com> wrote:
> > On Oct 18, 2007, at 11:22 AM, David A. Black wrote:

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Hi --

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

I consider a class's instances to be part of the world outside the
class, in particular the class as a first-class object with all the
rights and privileges appertaining thereunto (or whatever they say
when they give you a diploma :slight_smile: I don't think the decision on a
class's part to expose state via (its own) attr_* methods is different
whether it bears on objects that happen to be its own instances, or
objects that don't.

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 :slight_smile:

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

But if objects started seeing each other's instance variables, then
we'd have to come up with a replacement for the thing that today we
call "instance variables". At least, I definitely think that there
should be a way to maintain state strictly per-object; and if @vars
didn't do it, something else would. So I prefer to think of @vars as
already being the "something else" :slight_smile:

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.

It depends what you mean by "normal" :slight_smile: Since Ruby isn't a prototyped
language, I wouldn't apply those design norms to it. Actually, #clone
and #dup sound much better, as a way of generating new objects that
essentially copy existing class objects, than instantiating classes. I
don't expect C.new to be like C, whereas I do expect C.clone to be.

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.

I'm not sure I consider a singleton class or a child class exactly an
"attribute" of a class (though that's another one of those terms that
can slip around a lot). I'm also not convinced of the special bond
between a class and its instances, especially any kind of bond of
identity.

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.

But then they learn about self and instance variables and they stop
expecting it :slight_smile: But I'm not sure I'm following exactly what you mean
here -- i.e., what your vision is of how it should behave as opposed
(?) to how it does behave.

David

···

On Sat, 20 Oct 2007, ara.t.howard wrote:

On Oct 19, 2007, at 5:09 AM, David A. Black wrote:

--
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!

I'm still not buying it. I can't believe there's a scenario in Rails,
to use your example, where a class variable is initialized in a base
class after it was first claimed by a derived class, thereby creating
two copies - or maybe I just don't want to believe it! :slight_smile:

I'm not saying class variables are perfect, but this notion of a
subclass claiming a class variable before the superclass initializes
it still seems contrived to me.

···

On Oct 19, 1:51 pm, "Rick DeNatale" <rick.denat...@gmail.com> wrote:

On 10/19/07, Brian Adkins <lojicdot...@gmail.com> wrote:

> On Oct 19, 12:22 pm, "Rick DeNatale" <rick.denat...@gmail.com> wrote:

> > So what was my point here? Oh yes. I was talking about what happens
> > when you make certain changes to the inheritance structure. If we
> > had:

> > class A
> > end

> > class B < A
> > @@cv = 42
> > end

> > And then later
> > class A
> > @@cv = 57
> > end

> > Ruby currently at most warns about a conflict then goes ahead and adds
> > a new class variable to A, leaving the existing one in C.

> Ok, you've used a similar example multiple times now, so my curiosity
> is getting the best of me - do you actually code this way, or are you
> trying to come up with a contrived example to show the alleged
> problems with class variables? Wouldn't you typically initialize the
> class variable in the "superest" class only?

Imagine that this code is not all in one file, but is spread out
between the implementation and usage of a framework like, say, rails.

it's not a vision - it's production code, gem released for several years:

   http://codeforpeople.com/lib/ruby/traits/traits-0.9.2/README

you can ignore all the features except inheritance. here's an example of how it works:

cfp:~ > cat a.rb
require 'traits'

class C
   class << self
     trait 'a' => 42
   end
end

class B < C
end

class A < B
end

B.a 42.0

p A.a
p B.a
p C.a

cfp:~ > ruby a.rb
42
42.0
42

since writing traits i've grown to prefer my attributes library, which is < 100 lines of code and allows one to setup 'smart' class variable inheritance in only a few lines of code

cfp:~ > cat a.rb
require 'attributes'

class C
   class << self
     attribute('a'){
       catch(:value){
         (ancestors - [self]).each do |ancestor|
           break unless self <= ancestor
           ancestor.module_eval{ throw :value, @a if defined? @a }
         end
         default = 42
       }
     }
   end
end

class D < C; end
class E < D; end
class F < E; end

p D.a
E.a = 42.0
p F.a

cfp:~ > ruby a.rb
42
42.0

i think either for of class variable inheritance is preferable to the @@semantics. it's up in the air whether references or copies should be taken though.

regards.

a @ http://codeforpeople.com/

···

On Oct 19, 2007, at 7:27 PM, David A. Black wrote:

But then they learn about self and instance variables and they stop
expecting it :slight_smile: But I'm not sure I'm following exactly what you mean
here -- i.e., what your vision is of how it should behave as opposed
(?) to how it does behave.

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

Brian Adkins wrote:

I'm not saying class variables are perfect, but this notion of a
subclass claiming a class variable before the superclass initializes
it still seems contrived to me.

It happened to me once before. I don't remember the exact circumstances but it happened. Once in 4 years of ruby programming. Heh, what can you do, there's no language smart enough to fix all your bugs for you.

Hi --

But then they learn about self and instance variables and they stop
expecting it :slight_smile: But I'm not sure I'm following exactly what you mean
here -- i.e., what your vision is of how it should behave as opposed
(?) to how it does behave.

it's not a vision - it's production code, gem released for several years:

I meant I wasn't sure whether you were saying that you thought the
newbie take on instance variables was actually how you felt they
should work.

[examples of trait and attribute libraries]

i think either for of class variable inheritance is preferable to the @@semantics. it's up in the air whether references or copies should be taken though.

I definitely think they're better than @@this, since (as I understand
it) they don't involve blurring the line between and among the objects
to which they apply, except via the inheritance line, which I think is
more reasonable and useful than the class/instance line.

David

···

On Sun, 21 Oct 2007, ara.t.howard wrote:

On Oct 19, 2007, at 7:27 PM, David A. Black wrote:

--
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!