Class instance variable idiom

A

ara.t.howard

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/
 
R

Rick DeNatale

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.
Should 'class C' be 'class C < B' ?

Yes, thanks!
 
B

Brian Adkins

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

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

Daniel DeLorme

Brian said:
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.
 
D

David A. Black

Hi --

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 :) 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.
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" :)
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" :) 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 :) 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

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

ara.t.howard

But then they learn about self and instance variables and they stop
expecting it :) 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/
 
D

David A. Black

Hi --

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

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

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,534
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top