Class instance variable idiom

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.

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, 1:51 pm, “Rick DeNatale” [email protected] wrote:

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.

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 10/19/07, Brian A. [email protected] wrote:

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.

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


Rick DeNatale

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

Brian A. 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 –

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

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.

average ruby programmer and, when you are working with classes at that level
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.

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

Hi –

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

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

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/