Ruby 继承和类型化

Ruby inheritance and typing

我对 Ruby 中的一些基本概念有疑问,特别是子class 与超级class 的互换性。

根据 classes 上的 Ruby 文档,"Class" 继承自 "Module"。 https://ruby-doc.org/core-2.5.3/Class.html

class MyClassTest end
MyClassTest.is_a? Module # => true

但是,当尝试使用 module 关键字重新打开使用关键字 class 定义的 class 时,您会得到一个 TypeError,表明 class 不是模块。

class MyClassTest end
module MyClassTest end # => TypeError: MyClassTest is not a module

这个 SO 问题围绕子 classes 与子类型进行了一些精彩的讨论,但我认为它让我想到了更多问题: Why can't classes be used as modules?

一般来说,由于Ruby是动态类型的,我对TypeErrors的存在感到困惑。

具体来说,在这种情况下,我对 Ruby 继承如何导致无法用 subclass 替换 superclass 的 TypeError 感到非常困惑。在我看来,subclassing 等同于 Ruby 中的子类型,因为 subclass 将继承 super[=39= 的接口(方法和 public 属性) ].

我目前的猜测是,当某些断言失败时,核心 Ruby 库会引发 TypeError,而这些 TypeError 不一定与 Ruby 的动态类型系统有任何关系,也就是说打字不是 Ruby 中的第一个 class 概念。链接的 SO 问题提出了关于具有多个 class 继承的菱形问题的优秀观点,因此 Ruby 会阻止模块和 classes 在使用 [=13] 时的可互换使用是有道理的=] 或 class 关键字。不过,感觉我对Ruby.

的理解有些矛盾

当预期 "Module" 对象时,"Class" 输入如何导致 TypeError?

基本断言是

  • Class 是一个 Class (Class.is_a?(Class) #=> true)
  • Class 是一个 Module (Class.is_a?(Module) #=> true)
  • class Class 的实例是 Class (Class.new.is_a?(Class) #=> true)
  • class Class 的实例是 Module (Class.new.is_a?(Module) #=> true)
  • Module 是一个 Class (Module.is_a?(Class) #=> true)
  • 凭借Module是一个Module (Module.is_a?(Module) #=> true)
  • class Module 的实例是 Module (Module.new.is_a?(Module) #=> true)
  • 但是 class Module 的实例不是 Class (Module.new.is_a?(Class) #=> false)
  • class 的实例 ClassClass 的实例,但不是 class Module 的实例(Class.new.instance_of?(Module) #=> false )

module 是 class Module 实例的声明,正如 class 是 class Class.

如果这是一种方法,它可能看起来像

def module(name,&block)
  raise TypeError if defined?(name) && !const_get(name).instance_of?(Module)
  obj = defined?(name) ? const_get(name) : const_set(name, Module.new)
  obj.instance_eval(&block)
end

TypeError 的存在是为了防止歧义。

与您的情况一样,通过使用 class MyClassTest,您已经创建了 class Class 的一个实例,该实例称为 MyTestClass

如果您也被允许在同一个全局上下文中使用 module MyTestClass,那么在使用过程中我将不知道在调用 MyClassTest 时我是否会调用 ClassModule

基本(非常基本)区别是 Class 可以实例化(有实例)但是 Module 不能。

例如

Class.new.new #creates an instance of the anonymous class created by Class.new
Module.new.new # results in NoMethodError

我认为混淆的第一点是用法定义之间的区别。

下面的代码定义一个class:

class C; end

如果我看到上面的代码,希望以后能够实例化C:

C.new

但是,假设 C 已经被定义为一个模块:

# In one file:
module C; end

# Later in a different file:
class C; end    # C is actually a module

C.new           # fails

Ruby 在 C 被重新定义为 class 的地方出现了问题(C 的定义冲突),而不是让程序继续到使用 C 的地方。

尽早发现问题的好处通常是越早发现错误,就越容易找到并修复其根本原因(在这个例子中,也许 C 应该是 class所有,因此真正的问题是 module C 定义)。

你的第二个问题是,我认为,为什么 class 不能 always 用作模块,例如为什么禁止以下内容:

class C; end
class A
  include C
end

我认为,答案是编程语言以 概念 开始,然后使用各种构造 实现 。我们可以描述 classes 和模块如下:

  • A class 表示具有数据和行为的对象(classic OOP 定义)。
  • 模块是行为的集合。

include 关键字使用模块的 行为 扩展了 class。原则上,可以只采用 class 的方法并将它们添加到另一个 class。但是这个操作没有意义因为class是一个对象和它的行为在一起。只采取行为违背了 class.

的概念

还有其他编程语言在这个问题上采取不同的立场。例如,在 JavaScript 中,任何函数都可以从任何 class 中取出,并使用任何其他 class 的实例调用。这在某些情况下很方便,但在其他情况下却很难调试。