Class variables - a surprising result

Obviously there’s some confusion though Matz.
In most languages (C++, Java, Delphi, etc.), class variables (static’s)
are scoped by the class, not the entire inheritance chain.
It’s just… different. Once people understand it, it’s fine, bug I
definitely don’t think it’s obvious.

···

-----Original Message-----
From: Yukihiro Matsumoto [mailto:matz@ruby-lang.org]
Sent: Thursday, August 21, 2003 10:50 AM
To: ruby-talk ML
Subject: Re: Class variables - a surprising result

Hi,

In message “Re: Class variables - a surprising result” on 03/08/21, David Heinemeier Hansson david@loudthinking.com writes:

Class variable allocation is per class scope, not per subclass scope.

Sub1, Sub2 and Sup really do share their class variables. Now don’t
ask me for the “why exactly”…

I was fooled by this as well. I don’t know why I intuitively would
think that a subclass would get its own scope, but I did. Having your
own scope as a subclass would be a really neat addition, though. But
perhaps a reason from Matz would shed light?

They are variables. If you can’t update values, how they can be
variables.

class Sup
 @@x = "A"          # declare @@x, set @@x as "A"
 def test 
  print @@x 
 end
end

class Sub1 < Sup
 @@x = "B"          # set @@x as "B"
end

class Sub2 < Sup
 @@x = "C"          # set @@x as "C"
end

Sup.new.test        # print the value of @@x ("C")
Sub1.new.test       # ditto.
Sub2.new.test       # ditto.

Class variables are like global variables whose scope is limited to the
inheritance tree.

						matz.

Hi,

···

In message “Re: Class variables - a surprising result” on 03/08/22, “Bennett, Patrick” Patrick.Bennett@inin.com writes:

In most languages (C++, Java, Delphi, etc.), class variables (static’s)
are scoped by the class, not the entire inheritance chain.

As far as I know, C++ and Java do not have class variables. They have
static member variables, which can be either private, protected, or
public. I know nothing about Delphi.

						matz.

eyebrow.raise

This is interesting, because I would have said, “Those are class
variables, they just don’t CALL them that.”

I learned OOP from Eiffel (Meyer’s OOSC book) – just the terminology
and concepts, I never actually coded in Eiffel.

Don’t I recall hearing that Ruby’s class variables are based on those
of Smalltalk? (I don’t know Smalltalk.)

If so, maybe the confusion is just related to mildly differing
flavors of OOP.

For the record, I have always been confused by Ruby’s class variables
also, thinking that they should belong to the class and not the
hierarchy.

Hal

···

----- Original Message -----
From: “Yukihiro Matsumoto” matz@ruby-lang.org
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Thursday, August 21, 2003 1:47 PM
Subject: Re: Class variables - a surprising result

Hi,

In message “Re: Class variables - a surprising result” > on 03/08/22, “Bennett, Patrick” Patrick.Bennett@inin.com writes:

In most languages (C++, Java, Delphi, etc.), class variables (static’s)
are scoped by the class, not the entire inheritance chain.

As far as I know, C++ and Java do not have class variables. They have
static member variables, which can be either private, protected, or
public. I know nothing about Delphi.

… . . which are not inherited, which is why it is surprising to many
that Ruby’s @@variables are inherited.

-Mark

···

On Fri, Aug 22, 2003 at 03:47:33AM +0900, Yukihiro Matsumoto wrote:

Hi,

In message “Re: Class variables - a surprising result” > on 03/08/22, “Bennett, Patrick” Patrick.Bennett@inin.com writes:

In most languages (C++, Java, Delphi, etc.), class variables (static’s)
are scoped by the class, not the entire inheritance chain.

As far as I know, C++ and Java do not have class variables. They have
static member variables,

As far as I know, C++ and Java do not have class variables. They have
static member variables,

… . . which are not inherited, which is why it is surprising to many
that Ruby’s @@variables are inherited.

What do you mean by “not inherited”… I’m sure you’re correct, but I’m
confused:

C:\tmp>cat Sub.java

class Base {
static int B = 1;
}

public class Sub extends Base {
public static void main(String args) {
System.out.println("Sub.B = " + Sub.B);
}
}

C:\tmp>javac Sub.java

C:\tmp>java Sub

Sub.B = 1

Hal E. Fulton hal9000@hypermetrics.com skrev den Fri, 22 Aug 2003
03:58:13 +0900:

For the record, I have always been confused by Ruby’s class variables
also, thinking that they should belong to the class and not the
hierarchy.

FTR, I’ve also been bitten by this. For me I think the problem
is their name; “class variables” just sounds like they would belong
to only the class. But of course subclasses also “belong” to that
class so there is some logic to it.

If they were more common I’d vote for adding more syntax (@@@x :))
but I tend to use both of them seldomly so don’t really see the need.

Maybe this “quirk” should be added (or already have been) to some wiki page
for newcomers? Or is it obvious to everyone else?

Regards,

Robert Feldt

Mark J. Reed wrote:

… . . which are not inherited, which is why it is surprising to many
that Ruby’s @@variables are inherited.

I’m one of them too. Is there a reason that they’re implemented the way
they are? Is it ease of implementation? Based on a different OO
paradigm? To me it violates the Principle of Least Surprise.

If I create a class inheriting from FooBar, it seems to me that it’s
dangerous that I can break FooBar and all of its other subclasses by
doing what appears to be “declaring a class variable” because in reality
I’m “changing a class variable defined in a superclass”.

Ben

Hmmm … it looks like they are inherited here …

// Begin Example ------------------------------------------------------
class A {
static int avar = 0;
}

class B extends A {
}

public class Demo {
public static void main(String args) {
System.out.println ("A.avar = " + A.avar); // => 0
System.out.println ("B.avar = " + B.avar); // => 0
B.avar = 10;
System.out.println ("A.avar = " + A.avar); // => 10
System.out.println ("B.avar = " + B.avar); // => 10
}
}
// End Example -------------------------------------------------------

(Sorry for posting Java code in a Ruby mailing list).

The truth is that Java doesn’t have “class variables” (although they are
sometimes called that). Java has static variables that are scoped by
the class. If you redeclare a variable in B, then it will hide the
variable in A.

Since you don’t declare variables in Ruby, you don’t get the “hiding”
effect.

···

On Thu, 2003-08-21 at 15:14, Mark J. Reed wrote:

On Fri, Aug 22, 2003 at 03:47:33AM +0900, Yukihiro Matsumoto wrote:

As far as I know, C++ and Java do not have class variables. They have
static member variables,

… . . which are not inherited, which is why it is surprising to many
that Ruby’s @@variables are inherited.


– Jim Weirich jweirich@one.net http://onestepback.org

“Beware of bugs in the above code; I have only proved it correct,
not tried it.” – Donald Knuth (in a memo to Peter van Emde Boas)

You’re only confused because you’re assuming I’m correct. :slight_smile: I
was mistaken; I was thinking of other oddities with static members
and inheritance.

-Mark

···

On Fri, Aug 22, 2003 at 04:23:12AM +0900, Michael Campbell wrote:

What do you mean by “not inherited”… I’m sure you’re correct, but I’m
confused:

My earlier post showed that Java’s static variables
behave exactly the same way. The only difference is
that Java offers a few more choices:

  • Make the variable private so that it isn’t
    inherited.

  • Override the variable in an ancestor by DECLARING it
    afresh. However, you still have to be careful because
    any inherited methods will refer to the Static
    variable in the base class.

— Michael Campbell michael_s_campbell@yahoo.com
wrote: > > > As far as I know, C++ and Java do not
have class

···

variables. They have

static member variables,

… . . which are not inherited, which is why it is
surprising to many
that Ruby’s @@variables are inherited.

What do you mean by “not inherited”… I’m sure
you’re correct, but I’m
confused:

C:\tmp>cat Sub.java

class Base {
static int B = 1;
}

public class Sub extends Base {
public static void main(String args) {
System.out.println("Sub.B = " + Sub.B);
}
}

C:\tmp>javac Sub.java

C:\tmp>java Sub

Sub.B = 1


Want to chat instantly with your online friends? Get the FREE Yahoo!
Messenger http://uk.messenger.yahoo.com/

What do you mean by “not inherited”… I’m sure you’re correct, but I’m
confused:

You’re only confused because you’re assuming I’m correct. :slight_smile: I
was mistaken; I was thinking of other oddities with static members
and inheritance.

Well, I think that in Java/C++ they’re inherited but not shared.
In Ruby they’re shared.

Of course, there’s an ugly trick that gets around this… can anyone
guess it? David showed it to me once.

Hal

···

----- Original Message -----
From: “Mark J. Reed” markjreed@mail.com
Newsgroups: comp.lang.ruby
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Thursday, August 21, 2003 3:35 PM
Subject: Re: Class variables - a surprising result

On Fri, Aug 22, 2003 at 04:23:12AM +0900, Michael Campbell wrote:


Hal Fulton
hal9000@hypermetrics.com

Hal E. Fulton wrote:

Well, I think that in Java/C++ they’re inherited but not shared.
In Ruby they’re shared.

Of course, there’s an ugly trick that gets around this… can anyone
guess it? David showed it to me once.

Are you referring to the “class << self.class” trick (alluded to by Matz
and others elsewhere in the thread)?

If you think it’s ugly, you can always wrap methods around it to make it
look more like normal access, or
at least more normal by Java standards. :slight_smile:

Code below.

  • Dan

class Module
# Java style class variables
# if class Foo defines a static variable, all subclasses can see it
# but it can be overrided by re-declaring, so to speak.

···

# static_var :foo  (re-)declares a static class variable
# static variable are modifiable in subclasses unless redeclared, as 

they
# are in Java
def static_var(*args)
static_var_writer *args
static_var_reader *args
end

def static_var_reader(*args)
    args.each do |symbol|
        eval <<-EOF
            def #{name}.#{symbol.id2name}
                class << #{name}
                    @#{symbol.id2name}
                end
            end
        EOF
    end
end

def static_var_writer(*args)
    args.each do |symbol|
        eval <<-EOF
            def #{name}.#{symbol.id2name}=(value)
                class << #{name}
                    proc do |e| @#{symbol.id2name} = e end
                end.call value
            end
        EOF
    end
end

# More restrictive class instance variables
# These variables are shared by all instances of the class, but
# subclasses cannot see or modify them through the accessors. The
# accessors will still exist, but they will deal with the subclass'
# own class instance variable, and not interfere with that of the
# superclass
def cinst_var(*args)
    cinst_var_writer *args
    cinst_var_reader *args
end

def cinst_var_reader(*args)
    args.each do |symbol|
        class_eval <<-EOF
            def self.#{symbol.id2name}
                class << self
                    @#{symbol.id2name}
                end
            end
        EOF
    end
end

def cinst_var_writer(*args)
    args.each do |symbol|
        class_eval <<-EOF
            def self.#{symbol.id2name}=(value)
                class << self
                    proc do |e| @#{symbol.id2name} = e end
                end.call value
            end
        EOF
    end
end

end

class Foo
static_var :var
cinst_var :var2
end

class Bar < Foo
end

class Baz < Foo
static_var :var
end

Foo.var = 5

p Foo.var
p Bar.var
p Baz.var

Bar.var = 6

p Foo.var
p Bar.var
p Baz.var

Baz.var = 7

p Foo.var
p Bar.var
p Baz.var

Foo.var2 = 8

p Foo.var2
p Bar.var2
p Baz.var2

Bar.var2 = 9

p Foo.var2
p Bar.var2
p Baz.var2

Baz.var2 = 10

p Foo.var2
p Bar.var2
p Baz.var2

Hi –

Hal E. Fulton wrote:

Well, I think that in Java/C++ they’re inherited but not shared.
In Ruby they’re shared.

Of course, there’s an ugly trick that gets around this… can anyone
guess it? David showed it to me once.

Are you referring to the “class << self.class” trick (alluded to by Matz
and others elsewhere in the thread)?

No, he’s referring to this:

class A
end

class B < A
@@x = 1
end

class A
@@x = 2
end

class B
p @@x # prints 1, not 2 – A and B are not sharing @@x
end

If you think it’s ugly, you can always wrap methods around it to make it
look more like normal access, or
at least more normal by Java standards. :slight_smile:

I knew there was some reason I didn’t want to learn Java… :slight_smile:

Code below.

Still scanning it, but one quick comment: I notice you don’t use
public/private/protected. Are there places where that might give you
the same kind of functionality?

David

···

On Fri, 22 Aug 2003, Dan Doel wrote:


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

Hi –

More feedback, post-immersion :slight_smile:

class Module

[skipping ahead a bit]

# More restrictive class instance variables

I’m not sure what you mean – they’re already pretty restrictive (see
below).

# These variables are shared by all instances of the class, but
                                      ^^^^^^^^^

(Do you mean subclasses?)

# subclasses cannot see or modify them through the accessors. The
# accessors will still exist, but they will deal with the subclass'
# own class instance variable, and not interfere with that of the
# superclass

I think what you’re describing is what already happens with class
instance variables and accessors:

irb(main):001:0> class A; class << self; attr_accessor :a; end; end
=> nil
irb(main):002:0> A.a = 1
=> 1
irb(main):003:0> class B < A; end
=> nil
irb(main):004:0> B.a = 2
=> 2
irb(main):005:0> A.a
=> 1

B inherits the methods a and a= from A, but those methods (which get
and set @a) operate in the context of B when called on B – that is,
they get and set B’s @a, not A’s @a. To get at A’s @a you’d have to
ask A.

David

···

On Fri, 22 Aug 2003, Dan Doel wrote:


David Alan Black
home: dblack@superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

You should put that up on
http://www.rubygarden.org/ruby?StandardClassExtensions - nobody’s done a
StandardClassExtensions/Module yet.

martin

···

Dan Doel djd15@po.cwru.edu wrote:

If you think it’s ugly, you can always wrap methods around it to make it
look more like normal access, or
at least more normal by Java standards. :slight_smile:

Code below.

dblack@superlink.net wrote:

No, he’s referring to this:

class A
end

class B < A
@@x = 1
end

class A
@@x = 2
end

class B
p @@x # prints 1, not 2 – A and B are not sharing @@x
end

Heh, oh, that is rather bad.

I knew there was some reason I didn’t want to learn Java… :slight_smile:

It’s only more Java-esque because the “variables” are

Class.var_name

Instead of:

@@var_name

You could invent symbols for all these idioms ($@var_name, @@@var_name,
or whatever) but that’d
require changing the interpreter.

Code below.

Still scanning it, but one quick comment: I notice you don’t use
public/private/protected. Are there places where that might give you
the same kind of functionality?

Realistically, you would want to use public/private/protected. If you
want to keep in the spirit of
Ruby, the code should probably be modified so that all these accessors
are private. If you’re more
of a Java person, I suppose you could modify it so that you could
specify the access control level.
I left that part out because it’s not that hard to add, it’s a bit of a
side issue, and I don’t really know
how everyone would want it to work (and I’m not enough of a language
theorist to make that decision
for everyone :slight_smile: ).

In my first attempts, I coded up a system where if you used the reader,
it would look in the current
class, and if that wasn’t available, it would look in superclasses, but
if you used the writer it would
only affect the current class. However, that seemed too weird for me.
I think the two I gave earlier
are probably the most understandable.

I’m not sure what you meant by giving “the same kind of functionality.”
They set method access
control, and I’m not sure what that has to do with making
class/static/class-instance varibles. But
I could be missing something.

Cheers.

  • Dan

I suppose I was a bit ambiguous.

I meant that the class instance variables are more strict that the
Java-style-static variables.

cinst_var methods make normal class instance variables.

  • Dan

Also, I hadn’t thought of doing:

class << self
attr_accessor :var
end

That’s probably easy enough that it makes my second set of code pointless.
It’s apparently equivalent to:

cinst_writer(*args)
class << self
method(:attr_writer)
end.call *args
end

And so on.

Makes things quite a bit easier, I guess. :slight_smile:

  • Dan

Hi,

···

In message “Re: Class variables - a surprising result” on 03/08/22, dblack@superlink.net dblack@superlink.net writes:

No, he’s referring to this:

class A
end

class B < A
@@x = 1
end

class A
@@x = 2
end

class B
p @@x # prints 1, not 2 – A and B are not sharing @@x
end

Consider this oddity as a bug to be found in the future interpreter,
not a feature. You get the warning already.

						matz.