Issue #14541 has been updated by shevegen (Robert A. Heiler).


> Inheritance of @@class variables is precisely what makes them useful,
> since they're truly one single global value.

You can always find pros/cons. Some will find a feature useful, others
will not. My personal opinion, for example, is that I find @@vars
largely unnecessary, so I don't use them in my own code; another smaller
reason is that I find the two @ not so elegant. Others may have another
opinion and already expressed so, e. g. at a developer meeting some 
months ago - I think that will very often be the case where people have 
different opinions. And ultimately matz decides and he already decided
and explained here, and elsewhere. :)

I can achieve "inheritance" via @instance variables and specifying 
access to it via custom code too (well, methods), but I think it is not a very
strong argument to refer to it as what makes @@class variables that useful
to begin with, because ruby is so dynamic that inheritance and access-specifiers
aren't a very strict concept. For example, we can obtain and change variables at
"runtime" at any moment in ruby as-is, e. g. instance_variable_get/set
and so forth. Ruby is ultimately a "tool-box" of code and it has another concept
for both OOP but also how to interface with it (from the human side), compared
to, say, C++ and Java.

> And in addition to what matz points out rubocop has the
> Cop::Style::ClassVars warning.

Rubocop, and neither the style guide, are not designing ruby though. It's great
that rubocop exists; and it is great that it can be of help keeping code bases
sane. But that is only one part - the other part is the design of ruby as such in
itself. The ruby parser can also be thought of some kind of "style guide", e. g.
what it enforces, or what warnings it will show, and so forth. It would be nice if
we could customize it a bit more in general when it comes to warnings/notifications,
a bit like rubocop - but keeping this simple, too. Rubocop can become a bit 
complicated if you look at all the different cop-rules that projects can use. I
like simplicity too.

> As someone who manages a 10 year old, 250,000 line long ruby
> codebase, please don't remove them or change the semantics.

I have been using ruby for a very, very, very long time but ... how
can you manage to write 250.000 lines of ruby code? I assume that
must have been written by more than one person. 

> The class variables we have now in our project have often been
> around for 10 years and while in some purist sense they are all
> technical debt, they're not doing any harm -- forcing me to have
> a flag day to fix them all would be a large waste of my time
> (some of them have been fixed as they've been found to be
> problematic and/or the code in question simply got cleaned
> up as part of a larger refactoring pass).

I think you need not worry - matz already said that the incompatibility
issue is a real one, so it is super-unlikely that class variables will be changed; most
definitely not  for ruby 3.0 but I think probably also not at a later time. So a
lot of this discussion here is mostly a purely hypothetical one. There have been
other ruby users who expressed that they use @@class vars and that they also
like them. There is no "wrong" use of code as such per se; people will use features
and functionality when it is made available.

I think very large code bases are always problematic, not just in regards to ruby
alone but in general.

Matz also mentioned several times in presentations that he wants to avoid changes
such as from ruby 1.8.x to (ultimately) ruby 2.0 as that was a pain point for quite a
few people. So I think when  discussing it, we should mostly refer to this as a purely
theoretical  discussion.

I would also like to propose to eventually close this particular issue here eventually
when eregon is ok with it - ideally before ruby 3.0 is released, simply to keep the
amount of issues a bit smaller. (We could always have another discussion in the
future after 3.0 but I think the chances for change here are quite low, and about
0% for ruby 3.0 anyway; possibly even 0% at a later time but who knows.)

By the way I also agree that it is not something that is hugely important from a
practical point of view - people who like class variables can use them; those
who don't like them can avoid them. Ultimately project owners can specify what
they need/accept in code bases that they maintain. I have a long list of ruby code
I would reject - others may probably feel similar in a different way about ruby code
they find acceptable and code they don't. :) (I actually found that one of the biggest
problem is the lack of documentation and comments - some people never write 
any comments and barely any documentation, and it is very often that code written
by them is either brilliant or totally awful. And in both cases comments/documentation
would help OTHER people immensely, if it is up-to-date and of high quality.)

----------------------------------------
Bug #14541: Class variables have broken semantics, let's fix them
https://bugs.ruby-lang.org/issues/14541#change-76060

* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
* ruby -v: ruby 2.6.0dev (2018-01-29 trunk 62091) [x86_64-linux]
* Backport: 2.3: UNKNOWN, 2.4: UNKNOWN, 2.5: UNKNOWN
----------------------------------------
Class variables have the weird semantics of being tied to the class hierarchy and being inherited between classes.
I think this is counter-intuitive, dangerous and basically nobody expects this behavior.

To illustrate that, we can break the tmpdir stdlib by defining a top-level class variable:

    $ ruby -rtmpdir -e '$SAFE=1; @@systmpdir=42; p Dir.mktmpdir {}'
    -e:1: warning: class variable access from toplevel
    Traceback (most recent call last):
    	3: from -e:1:in `<main>'
    	2: from /home/eregon/prefix/ruby-trunk/lib/ruby/2.6.0/tmpdir.rb:86:in `mktmpdir'
    	1: from /home/eregon/prefix/ruby-trunk/lib/ruby/2.6.0/tmpdir.rb:125:in `create'
    /home/eregon/prefix/ruby-trunk/lib/ruby/2.6.0/tmpdir.rb:125:in `join': no implicit conversion of Integer into String (TypeError)

Or even simpler in RubyGems:

    $ ruby -e '@@all=42; p Gem.ruby_version'
    -e:1: warning: class variable access from toplevel
    Traceback (most recent call last):
    	3: from -e:1:in `<main>'
    	2: from /home/eregon/prefix/ruby-trunk/lib/ruby/2.6.0/rubygems.rb:984:in `ruby_version'
    	1: from /home/eregon/prefix/ruby-trunk/lib/ruby/2.6.0/rubygems/version.rb:199:in `new'
    /home/eregon/prefix/ruby-trunk/lib/ruby/2.6.0/rubygems/version.rb:199:in `[]': no implicit conversion of String into Integer (TypeError)

So defining a class variable on Object removes class variables in all classes inheriting from Object.
Maybe @@systmpdir is not so prone to conflict, but how about @@identifier, @@context, @@locales, @@sequence, @@all, etc which are class variables of the standard library?

Moreover, class variables are extremely complex to implement correctly and very difficult to optimize due to the complex semantics.
In fact, none of JRuby, TruffleRuby, Rubinius and MRuby implement the "setting a class var on Object removes class vars in subclasses".
It seems all implementations but MRI print :foo twice here (instead of :foo :toplevel for MRI):

~~~ ruby
class Foo
  @@cvar = :foo
  def self.read
    @@cvar
  end
end

p Foo.read
@@cvar = :toplevel
p Foo.read
~~~


Is there any library actually taking advantage that class variables are inherited between classes? I would guess not or very few.
Therefore, I propose to give class variable intuitive semantics: no inheritance, they behave just like variables of that specific class, much like class-level instance variables (but separate for compatibility).

Another option is to remove them completely, but that's likely too hard for compatibility.

Thoughts?



-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>