实施严格的重载方法签名以防止滥用

Implementing rigorous overloaded method signatures that prevent misuse

我正在尝试实现具有相似但不同输入要求的方法的不同版本。来自具有静态和强类型的语言,我试图以这样一种方式编写这些方法,即目标用户(这是一个包)以意外方式使用这些方法的能力被最小化。该方法的不同版本具有不同数量的参数,我发现支持具有两个或更多参数的方法版本允许将废话输入到只需要一个参数的方法版本中。这是一个简单的例子:

setGeneric(
  "foo",
  function(a, b) {
    standardGeneric("foo")
  })

setMethod(
  "foo",
  signature(a = "numeric"),
  function(a) {
    abs(a)
  })

setMethod(
  "foo",
  signature(a = "numeric", b = "numeric"),
  function(a, b) {
    abs(c(a, b))
  })

它按预期使用以下输入工作(有些有效,有些无效,并且会按应有的方式抛出错误):

> foo(-1)
[1] 1

> foo(-1, -2)
[1] 1 2

> foo("cat")
 Error in (function (classes, fdef, mtable)  : 
  unable to find an inherited method for function ‘foo’ for signature ‘"character", "missing"’ 

> foo()
 Error in (function (classes, fdef, mtable)  : 
  unable to find an inherited method for function ‘foo’ for signature ‘"missing", "missing"’ 

> foo(-1, -2, "cat")
Error in foo(-1, -2, "cat") : unused argument ("cat")

但是在一种情况下,它的行为方式不应该是可以接受的:

> foo(-1, "cat")
[1] 1

它调用第一个方法签名并忽略第二个参数。这对用户来说可能是一个严重的逻辑错误,这对我来说是个问题,因为我的目标用户不是计算机科学家;大多数人不了解什么是逻辑错误,它们有多危险,或者如何注意它们。 在 R 中有没有一种方法可以设置方法,以便最后一个示例 foo(-1, "cat") 抛出错误,而不是让用户看起来一切都很好?

请注意,虽然我正在研究的不同版本的方法在根本上是相关的,但每个版本的实际实现却大不相同。我可以使用带有可选参数的函数,但这需要对 运行 完全不同的大块代码进行多次检查。我希望避免这种情况,因为它不是特别干净或优雅。

如果您按以下方式使用特殊的 "missing" 签名,您可以让它工作:

setGeneric(
  "foo",
  function(a, b) {
    standardGeneric("foo")
  })

setMethod(
  "foo",
  signature(a = "numeric", b = "missing"),
  function(a, b) {
    abs(a)
  })

setMethod(
  "foo",
  signature(a = "numeric", b = "numeric"),
  function(a, b) {
    abs(c(a, b))
  }
)

正在检查来电:

foo(-1)
#[1] 1

foo(-1, -2)
#[1] 1 2

foo(-1, "cat")
#Error in (function (classes, fdef, mtable)  : 
#  unable to find an inherited method for function ‘foo’ for
#  signature ‘"numeric", "character"’ 

foo("cat")
# Error in (function (classes, fdef, mtable)  : 
#  unable to find an inherited method for function ‘foo’ for
#  signature ‘"character", "missing"’ 

同样,foo()foo(-1, -2, "cat") 和以前一样失败。

正如您自己观察到的,如果您在方法中放入一些 print 语句,您会看到调用 foo(-1, "cat") 被分派到您的单参数方法。没有抛出错误的原因是,永远不需要和评估 b 参数的承诺。在这种情况下,我不太了解 S4 方法调度规则的细节,也不知道它是否符合预期。但是无论如何,根据 "missing" 签名,我认为方法的参数始终与泛型的参数匹配是一种很好的做法;我很确定 R CMD check 之类的东西如果不匹配就会抱怨。