Project

General

Profile

Feature #5148

Constant Namespace/Scoping in Dynamic Classes/Modules

Added by trans (Thomas Sawyer) about 8 years ago. Updated over 7 years ago.

Status:
Rejected
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:38723]

Description

When defining a dynamic class or module, the constants defined within it are not kept within it's namespace, but are tied to the outer scope's namespace.

c = Class.new do
  module M; end
end
#=> #<Class:0xa601300>
c::M  #=> M                                                                   
M     #=> M

This presents serious limitations in designing domain specific languages that take advantage of Ruby's class system. For instance in creating a test framework that uses blocks to defines testcases it is not possible to isolate fixture constants from those of other testcases. E.g.

describe "fancy mixin" do
  class Foo
    include FancyMixin
  end
  ...
end

describe "unfancy mixin" do
  class Foo
    include UnfancyMixin
  end
  ...
end

Foo in the unfancy testcase will use Foo in the fancy testcase, causing unexpected issues --especially as these testcases may be defined in different files and coded by different people.

History

#1

Updated by nobu (Nobuyoshi Nakada) about 8 years ago

  • Tracker changed from Bug to Feature

Updated by Eregon (Benoit Daloze) about 8 years ago

Hi,
On 2 August 2011 17:01, Thomas Sawyer transfire@gmail.com wrote:

Bug #5148: Constant Namespace/Scoping in Dynamic Classes/Modules
http://redmine.ruby-lang.org/issues/5148

When defining a dynamic class or module, the constants defined within it are not kept within it's namespace, but are tied to the outer scope's namespace.

c = Class.new do
module M; end
end
#=> #Class:0xa601300
c::M #=> M
M #=> M

I also noticed that recently.
It seems inconsistent to me.
Let's have some code:
https://gist.github.com/1129567

Constant scoping seems to depend on the the fact it is defined in a
"dynamic" scope or not.
But one can force to define on a specific scope by doing scope::Const
= value (like self::C = value in a Class.new block).

I think the constant definition scope should always be the current
scope (as you would always add "self::" before the constant name).
Also, this should be true for constant resolution, I should not need
self.class::CONST, in a parent class to resolve a child class's
constant.

To "solve" your problem, you could use self::

describe "unfancy mixin" do
class self::Foo # not at toplevel
include UnfancyMixin
end
# Foo # uninitialized constant Foo (NameError)
self::Foo # => #Class:0x00000100857ba8::Foo
const_get :Foo # => #Class:0x00000100857ba8::Foo
end

But it has an immediate pitfall: you can not reference it by 'Foo',
you need 'self::Foo' or 'const_get :Foo'.

Variables and method calls are fully dynamic for
definition/resolution, in opposition to constants.

Can someone explain the rationale behind this logic ?

Updated by mame (Yusuke Endoh) over 7 years ago

  • Status changed from Open to Assigned
  • Assignee set to matz (Yukihiro Matsumoto)

Hello,

Matz, do you think it should be fixed?
Nobu, do you think it can be fixed?

--
Yusuke Endoh mame@tsg.ne.jp

Updated by nobu (Nobuyoshi Nakada) over 7 years ago

I don't think it will be accepted.

Updated by matz (Yukihiro Matsumoto) over 7 years ago

  • Assignee changed from matz (Yukihiro Matsumoto) to mame (Yusuke Endoh)

I think this change is a good idea, basically. But I am not sure how much influence it would have to existing Ruby programs, and implementations. For example, Nobu has already shown his concern. We have to experiment before accepting this proposal.

Matz.

Updated by mame (Yusuke Endoh) over 7 years ago

  • Status changed from Assigned to Rejected
  • Assignee deleted (mame (Yusuke Endoh))

Okay.

Thomas, or anyone interested, please create a patch yourself, experiment it, show an objective evaluation of the influence of this feature, and then, persuade matz again.

--
Yusuke Endoh mame@tsg.ne.jp

Also available in: Atom PDF