【问题标题】:scala coalesces multiple function call parameters into a Tuple -- can this be disabled?scala 将多个函数调用参数合并为一个元组——这可以禁用吗?
【发布时间】:2011-02-20 12:13:28
【问题描述】:

这在我的项目中违反了类型安全,因此我正在寻找一种方法来禁用它。似乎如果一个函数采用 AnyRef(或 java.lang.Object),您可以使用任何参数组合调用该函数,Scala 会将参数合并为一个 Tuple 对象并调用该函数。

在我的情况下,该函数不需要元组,并且在运行时失败。我希望这种情况会在编译时被发现。

object WhyTuple {
 def main(args: Array[String]): Unit = {
  fooIt("foo", "bar")
 }
 def fooIt(o: AnyRef) {
  println(o.toString)
 }
}

输出:

(foo,bar)

【问题讨论】:

  • 如果一个函数将AnyRef 作为参数,它期望参数是任何东西,不是吗?我的意思是即使 scala 没有自动将参数打包到一个元组中,你仍然可以显式地传递一个元组,这当然是完全类型安全的,因为你的函数可以接受任何东西,而元组是任何东西。如果您的函数只能处理某些类型的参数,则应将其声明为仅接受这些类型的参数或动态检查参数类型。
  • 那么它期望什么?您可能必须将其分解为各个专家。当然,如果您期望除了 Tuple 之外的任何东西,那也无济于事。
  • 很公平......但通常合同(在其他编程语言中)是如果一个函数需要 1 个参数并用 2 个参数调用,那么编译将失败。
  • 隐式可以这样做吗?
  • 你能避免使用 AnyRef 吗?您正在将 Scala 类型系统扔出窗外 :) 它可能会带来一些性能损失,但是您是否考虑过使用结构类型?

标签: syntax scala tuples tuple-packing


【解决方案1】:

这里根本没有隐式或 Predef —— 只是很好的老式编译器魔法。您可以在type checker 中找到它。我现在无法在规范中找到它。

如果你有足够的动力,你可以在编译器中添加一个 -X 选项来防止这种情况发生。

或者,您可以避免编写接受 TupleN 超类型的 arity-1 方法。

【讨论】:

    【解决方案2】:

    这样的事情怎么样:

    object Qx2 {
        @deprecated def callingWithATupleProducesAWarning(a: Product) = 2
        def callingWithATupleProducesAWarning(a: Any) = 3
    }
    

    元组具有 Product 特征,因此任何传递元组的 callWithATupleProducesAWarning 调用都会产生弃用警告。

    【讨论】:

    • 可爱,但他可能想将Product 类型的值(考虑任何案例类)传递给方法。
    • 没错 - 我很懒惰。如果您需要在正常使用中传递产品,我认为您必须为要保护的所有 TupleN 类型添加 n 个单独的方法。
    【解决方案3】:

    编辑:根据比我更了解的人的说法,以下答案实际上是错误的:请参阅this answer。感谢Aaron Novstrup 指出这一点。

    这实际上是解析器的一个怪癖,而不是类型系统或编译器。 Scala 允许在没有括号的情况下调用零参数或一参数函数,但不允许调用具有多个参数的函数。所以就像Fred Haslam says,你写的不是一个带有两个参数的调用,它是一个带有一个元组值参数的调用。但是,如果方法 确实 采用两个参数,则调用 是两个参数的调用。似乎代码的含义会影响它的解析方式(这有点糟糕)。

    至于你实际上可以做些什么,这很棘手。如果该方法确实需要两个参数,那么这个问题就会消失(即,如果有人错误地尝试用一个或三个参数调用它,他们会得到一个编译错误,如你所料)。不要以为您一直推迟添加到该方法中的一些额外参数? :)

    【讨论】:

    • 仅供参考,如果您向 call 添加更多参数,您最终会得到更高级别的元组: fooIt("foo", "bar", "jam") 产生 Tuple3 . {评论调用,而不是方法签名}
    • 不幸的是,这个答案最终获得了最高票数(在发布此消息时)。这实际上是不正确的——见thisthis
    【解决方案4】:

    编译器能够解释没有圆括号的方法。所以 fooIt 中的圆括号表示元组。你的调用是一样的:

    fooIt( ("foo","bar") )
    

    话虽如此,如果您使用 Some(AnyRef) 或 Tuple1(AnyRef) 等包装器,您可以使方法排除调用,检索值。

    【讨论】:

    • 除非我遗漏了什么,否则这与其说是对问题前提的总结,不如说是一个答案。
    【解决方案5】:

    我认为 Predef 中 (x, y) 的定义是负责任的。 “-Yno-predefs”编译器标志可能有一些用处,假设您愿意手动导入您需要的任何隐式。我的意思是你必须在所有地方添加 import scala.Predef._。

    【讨论】:

      【解决方案6】:

      您是否还可以添加一个双参数覆盖,这会阻止编译器应用语法糖?通过使类型适当模糊,您不太可能得到误报。例如:

      object WhyTuple {
      
        ...
      
        class DummyType
      
        def fooIt(a: DummyType, b: DummyType) {
          throw new UnsupportedOperationException("Dummy function - should not be called")
        }
      }
      

      【讨论】:

      • 当然,如果您有很多这样的方法,或者如果您需要为超过 2 个参数执行此操作,那么这不是很有可扩展性。
      猜你喜欢
      • 1970-01-01
      • 2021-10-11
      • 2021-05-12
      • 2018-12-11
      • 1970-01-01
      • 1970-01-01
      • 2021-12-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多