为什么模块内部的 ruby 类 不能具有与常规 类 相同的作用域?

Why can't ruby classes inside modules have the same scope as regular classes?

我有一个内部有 class 的模块,但我发现如果不指定模块路径,内部的 class 无法访问封闭模块中的任何方法。

另一种看待它的方式是 module_function 似乎没有带入 class。

示例:

module MyMod
  def meaning
    42
  end

  class Foo
    def initialize
      puts "New Foo"
      puts "I can call #{MyMod.meaning} from here"
      puts "But I can't get to #{meaning} from here"
    end
  end

  def go
    puts "I can get to #{meaning} from here"
    bar = Foo.new
  end

  module_function :go, :meaning
  go
end

如果你运行这个,你会在“但我不能...”行中得到一个错误:

undefined local variable or method `meaning'

但是如果去掉整个文件周围的MyMod位,访问外层方法就没有问题了。

是否有一种无需提供完整路径即可访问这些内容的简单方法?

(我挑剔不包括完整路径的原因之一是因为我正在使用 http://redshift.sourceforge.net/script/ 来创建我可以实例化的 ruby 脚本 - 但它们不会有一个清晰简单的名称,如 'MyMod',我可以将其添加到路径中,实际上我必须传递范围,即使它只是 class)

的封闭范围

由于 Ruby 名称解析和方法查找的方式有效。当您在任何地方写 x 时,Ruby 将按以下顺序:

  1. 寻找一个x局部变量

  2. self.class

    中寻找x方法
  3. self.superclass

    中寻找x方法
  4. 重复步骤 3 直到 superclassnil

    Ruby 将在 class 层级中向上移动 试图找到方法 x 直到达到 BasicObject.

  5. 调用method_missing

    它的默认行为是引发您遇到的错误。

MyMod 仅包含 Foo,它不是 Foo 的 class 层次结构的一部分。这就是找不到方法的原因。

I have a module with a class inside,

不,你不知道。您有一个模块 definition,里面有 class definition,但是 不会使 class 一个嵌套的 class。 Ruby 没有嵌套 classes.

Ruby不是Beta, Scala, or Newspeak,Ruby.

中没有嵌套的class

嵌套模块或 class 定义 到另一个模块或 class 定义中 不会 创建嵌套关系在两个 classes / 模块之间。它只会使 常量 引用外部 class/ 模块命名空间的 class / 模块部分。

也就是说

没有区别
module Foo
  class Bar
  end
end

class Quux
end

module Foo
  Bar = Quux
end

嵌套常量,常量引用的对象

but I find that the class inside can't reach any of the methods in the enclosing module without specifying the module path.

正是因为没有“封闭模块”。有一个词法封闭模块定义,但在[之间创建任何形式的关系=16=] class 和 MyMod 模块。

Another way to look at it is that the module_function doesn't seem to carry into the class.

老实说,我不明白你的意思,一个方法“携带到 class”是什么意思,但是 Module#module_function is not magic. It does exactly what the documentation says it does: it takes an instance method of the module, copies it as an instance method of the singleton class of the module, and makes the original instance method private.

你可以阅读 its specification in the Ruby/Spec, it is fairly simple. Also, the Rubinius source code, both the basic version for booting the Rubinius kernel and the full version 相当可读。

最后,Module#module_function确实比

做的不多
class Module
  def module_function(*meths)
    meths.each do |meth|
      define_singleton_method(meth, &instance_method(meth).bind(self))
      private meth
    end

    self
  end
end

If you run this, you get an error on the "But I can't..." line of:

undefined local variable or method `meaning'

原因很简单:class Foo 及其任何超classes 都没有该名称的任何方法,所以 当然 你得到一个例外。

But if you remove the MyMod bits around the whole file, it has no problem accessing the outer method.

没有“外法”。 Ruby 没有类似 Beta 的嵌套 classes。这才是你误会的根本原因。您希望 Ruby 表现得像 Beta,但事实并非如此。 Ruby 从任何语言中汲取灵感,最值得注意的是(按重要性大致排序)Smalltalk、Lisp、Perl 和 Clu,但 Beta 不在其中。

这在这里工作的原因完全不同:

def meaning
  42
end

class Foo
  def initialize
    meaning
  end
end

在顶层定义的方法隐式定义为 Object. This is because the 的私有实例方法。由于Foo继承自Object,方法查找最终会找到Object中定义的meaning方法。

Is there an easy way to make these accessible without having to give the full path?

继承。例如,Module#append_features, which is called by Module#include 使模块成为包含 class 的超级 class,因此模块的所有实例方法都成为方法查找祖先链的一部分。

旁白:如果没有嵌套,那么 Module::nesting 有什么作用?嗯,是的,这是一个不幸的命名方法。术语“嵌套 class”或“嵌套模块”在 OO 中具有明确定义的含义,一直追溯到 Beta。但是这种方法是关于一种完全不同的嵌套:

指模块definitionslexical嵌套,not嵌套模块 本身 .

例如,这些模块定义都定义了完全相同的模块,但是定义text有不同的嵌套:

module Foo
  module Bar
    module Baz
      module Qux
        p Module.nesting
        #=> [Foo::Bar::Baz::Qux, Foo::Bar::Baz, Foo::Bar, Foo]
      end
    end
  end
end

module Foo
  module Bar
    module Baz::Qux
      p Module.nesting
      #=> [Foo::Bar::Baz::Qux, Foo::Bar, Foo]
    end
  end
end

module Foo
  module Bar::Baz
    module Qux
      p Module.nesting
      #=> [Foo::Bar::Baz::Qux, Foo::Bar::Baz, Foo]
    end
  end
end

module Foo::Bar
  module Baz
    module Qux
      p Module.nesting
      #=> [Foo::Bar::Baz::Qux, Foo::Bar::Baz, Foo::Bar]
    end
  end
end

module Foo
  module Bar::Baz::Qux
    p Module.nesting
    #=> [Foo::Bar::Baz::Qux, Foo]
  end
end

module Foo::Bar::Baz
  module Qux
    p Module.nesting
    #=> [Foo::Bar::Baz::Qux, Foo::Bar::Baz]
  end
end

module Foo::Bar
  module Baz::Qux
    p Module.nesting
    #=> [Foo::Bar::Baz::Qux, Foo::Bar]
  end
end

module Foo::Bar::Baz::Qux
  p Module.nesting
  #=> [Foo::Bar::Baz::Qux]
end

同样,这纯粹是词法模块定义的嵌套。模块本身没有嵌套;在所有这些情况下,模块本身都是相同的。这种嵌套 影响 常量查找

查找常量 ,然后 向上 继承链。

还有一个可以嵌套的实例:blocks create nested 词法作用域,而所有其他词法作用域(脚本,模块/class定义,和方法定义)不要嵌套。换句话说,块和 只有块 可以访问其封闭词法范围的局部变量(和 self)。