一个静态类型语言为什么无法像Ruby的method_missing一样实现?

12

我在静态类型语言方面没有太多经验(目前正在学习Scala并喜欢它!),但我注意到一件事情是这些语言似乎从来没有像Ruby的method_missing或ColdFusion的onMissingMethod那样的东西。是不是在静态类型语言中有某种固有限制阻止或使这个实现变得困难呢?


如果你想要了解一个静态类型语言内置类似功能的例子,可以看看D语言中的opDispatchopDispatch是一个模板方法,在编译时传递方法名和参数类型,因此你可以使用元编程来确定如何“运行”缺失的方法。当然,所有的东西仍然由编译器静态解析。相关SO问题 - anton.burger
5个回答

21

当然,可以添加处理它们的机制,但这与静态类型有所冲突:编译时确定您的程序不包含类型错误。

补充说明

Scala 2.9引入了一种实验性选项,使以前会导致静态类型检查失败的类型访问以动态方式进行处理。在2.10中对其进行了完善并将其变为非实验性选项,尽管仍由默认禁用的功能标志控制。您可以在SIP 17文档中阅读相关信息。有关Scala 2.10的“模块化”和功能标志的说明,请参见SIP 18


5
反射API也违反了这一点,但它们存在于大多数现代静态语言中并且被广泛使用。 - Matt Briggs
Randall所说的是正确的。无论好坏,“历史上”“编译时”与否一直是一个重大分歧。话虽如此,一些新的语言更接近于弥合这个差距(比如我想到的Scala,它有一个REPL)。 - rogerdpack
@rogerdpack:Scala绝对有一个REPL,虽然我不认为它是“动态”的一个方面,只是在使用语言时非常方便。 - Randall Schulz

2

Scala 2.9版本通过 scaladoc 提供了Dynamic特质,引入了这个功能。扩展Dynamic的类会获得神奇的方法applyDynamic(methodName, args),它类似于Ruby的method_missing。从Scala 2.9开始,必须启用-Xexperimental选项才能使用Dynamic


1
在静态类型语言中,成员函数是直接调用的。如果编译器无法确定要调用哪个成员函数,则程序将无法编译。从这个意义上说,方法调用是静态的。
在动态类型语言中,成员函数不是直接调用的。相反,调用代码向对象发送消息,然后语言运行时会找出如何处理该消息。例如,运行时将扫描具有相同名称的方法的对象,然后将扫描具有名称method_missing的方法的对象。从这个意义上说,方法调用是动态的。
C# 4将静态类型与动态类型结合起来。变量可以具有编译时类型dynamic。对此变量的任何方法调用都将像在动态类型语言中一样处理。对于具有静态类型的变量的任何方法调用都将像在静态类型语言中一样处理。
# static invocation, bound at compile time by the compiler
var s = 6;
s.ToString();

# dynamic invocation, handled at runtime by the CLR
dynamic d = 6;
d.ToString();

2
你在混淆概念。动态调度在几乎所有静态类型的面向对象语言中都存在。只有一些动态类型语言使用由Smalltalk先驱的消息传递模型。例如,Python不使用消息传递(相反,obj.method(args) 转换为“获取 objmethod 成员指向的函数,然后使用 obj 作为第一个参数和 args 调用它”。) - user395760
var与动态类型无关。它会导致编译器从赋给变量的值推断出静态类型。 - Simon Callan

1
仅翻译文本内容:

进一步回应Randall的帖子,虽然有可能实现,但这违背了静态范式的原因是它超越了“动态分派”。动态分派可以让你愉快地将调用分派到一个动态绑定到已知静态代码的函数。也就是说,编译器设置了从其角度确定性执行的运行时分派。

method_missing调用的作用是基于方法名称使用switch语句或等效语句(我相信您知道)来决定如何处理“捕获所有”的情况。因此,编译器不知道这里会发生什么。假设编译器做了以下操作:

if (function is known at compile time)
{
  provide static call or dynamic call to "some" derivation
}
else
{
  bind the function call to obj.method_missing(...) and pass in the "details"
}

然后你需要像这样提供method_missing

def method_missing(intendedFunctionName, arguments)
{
  if (intendedFunctionName is "X")
  {
    X may not need arguments, so just do something
  }
  else if (intendedFunctionName is "Y")
  {
    Y expects 5 arguments of varying types
    Throw exception if there isn't the right number or types
  }
  ... etc ...
}

要求编译器向您发送“任意”(即在编译时未知)类型的任意参数,并带有一个您可能没有考虑到的intendedFunctionName... 嗯,这不是很安全,而Scala旨在成为一种静态安全的语言。

是的,这是可行的,但不符合静态语言的精神。如果您真的想要那种类型的灵活性,多语言编程可能是您的朋友。

注意:Objective-C并非严格静态类型。代码执行的运行时引擎不允许像C/C++那样剥离或内联动态类型系统。


要求编译器向您发送“任意”(即在编译时未知)类型的参数,听起来就像C语言的可变参数一样,不是很安全。 - bk1e

0

Objective-C有"method_missing"(具体来说,forwardInvocationmethodSignatureForSelector),并且可以说是静态类型的。这是因为它将静态类型错误视为编译时的警告而不是错误,因为方法分派在运行时比C++中的虚拟方法更多(这就是为什么你可以有"method_missing")。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接