Issue #14541 has been updated by dsferreira (Daniel Ferreira).


 Eregon (Benoit Daloze) wrote:

> Moreover, manually defining class-level instance variables with
> 
> class MyClass
>   @classvar = :initial_value
>   class << self
>     attr_accessor :@classvar
>   end
> 
>   def some_use_of_classvar
>     MyClass.classvar ||= ...
>   end
> end
>
> is quite cumbersome, verbose and error-prone (to define the accessors on the singleton class).

This is one of those change requests that I have thought about for a long time already but didn't request it because I thought it would not be accepted.
I totally support it.

This kind of code example is kind of what I end up with (add private methods to it) in order to avoid all the quirks of current class variables.
Making class variables predictable by not giving them inheritance would be good but then how would we share state between the class hierarchy?
Using a top level class accessor? That works for me. Any situation where it would not apply? 

Hope this can be accepted.


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

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