Scala无法重载两个方法。

21 浏览
0 Comments

Scala无法重载两个方法。

为什么编译器会不允许既在编译时又在运行时都不会产生歧义的代码呢?尽管可能存在一些情况下方法重载可能会变得模糊不清,但是否有任何理由在这些情况下放宽限制呢?例如:\n

// 这样会失败:
def foo(a: String)(b: Int = 42) = a + b
def foo(a: Int)   (b: Int = 42) = a + b
// 这样也会失败。即使参数列表中没有相同类型的位置。
def foo(a: Int)   (b: Int = 42) = a + b
def foo(a: String)(b: String = "Foo") = a + b
// 这样是可以的:
def foo(a: String)(b: Int) = a + b
def foo(a: Int)   (b: Int = 42) = a + b    
// 这样也是可以的。
def foo(a: Int)(b: Int) = a + b
def foo(a: Int)(b: String = "Foo") = a + b
val bar = foo(42)_ // 这显然会报错 ...

\n是否有任何原因不能稍微放宽这些限制呢?特别是在将重载的Java代码转换为Scala默认参数时,这些限制是任意的,当用一个Scala方法替换了大量的Java方法后才发现这并不好。

0
0 Comments

问题出现的原因是Scala不能重载两个方法。解决方法是谨慎使用转换,因为它们适用于所有使用Either的情况,而不仅仅是foo函数。如果想要定义一个只被带有默认参数的函数接受的类型,则可以定义一个只被带有默认参数的函数接受的类型,这样一来,每当请求一个Either[A, B]值时,只有A和B被接受。然而,这种解决方案是否方便并不明确。

以下是一个可能的解决方案的示例代码:

implicit class DefaultEither[A,B](val either: Either[A, B]) extends AnyVal {
  def withDefault(default: B): Either[A, B] = either match {
    case Left(a) => Left(a)
    case Right(_) => Right(default)
  }
}
def foo(a: Either[Int, String], b: Int = 42) = a.withDefault("").fold(_ + b, _ + b)

在这个例子中,我们添加了一个名为DefaultEither的隐式类,它接受一个Either[A, B]对象,并提供了一个withDefault方法,该方法接受一个默认值,并根据Either的类型返回正确的值。在foo函数中,我们使用了这个新的withDefault方法来处理默认值的逻辑。这样一来,我们可以在不重载函数的情况下使用默认参数。

0
0 Comments

Scala中无法重载两个方法的原因是由于重载解析与默认参数的交互很难获得可读性和精确性的规范。当然,对于许多个别情况,比如这里所呈现的情况,很容易说出应该发生什么。但这还不够。我们需要一个规范来决定所有可能的边界情况。重载解析已经很难指定了。将默认参数加入其中将使其更加困难。这就是为什么我们选择将两者分开的原因。

感谢您的答案。可能让我困惑的事情是,在其他几乎所有地方,编译器只有在确实存在一些模糊性时才会抱怨。但在这里,编译器抱怨是因为可能会出现类似的情况导致模糊性。因此,在第一种情况下,只有在存在实际问题的情况下编译器才会抱怨,但在第二种情况下,编译器的行为不太精确,并为“看似有效”的代码触发错误。从最小惊讶原则来看,这有点不幸。

“很难获得可读性和精确性的规范是否意味着如果有人提供了一个好的规范和/或实现,当前的情况可能会得到改善?”即使有人提出了一个好的规范和实现,我觉得它也不会被接受,因为新的规范和实现比旧的要大,并且这样做会有持续的成本。在我看来,这个问题并不重要。

可以使用析取来解决这个限制,但这会带来运行时开销。我很好奇为什么很难指定重载解析。我希望有人能链接到规范的相关部分,这样读者就不必费力去找了。我找到了这个链接:https://issues.scala-lang.org/browse/SI-3595。

我对Scala将重载视为受人不喜和二等公民有一些评论。如果我们继续故意削弱Scala中的重载,我们正在用名称替代类型,这在我看来是一个倒退的方向。

如果Python可以做到,我不明白为什么Scala不能。复杂性的论点是一个好的论点:实现这个功能将从用户的角度使Scala变得更简单。阅读其他答案,您会看到人们发明非常复杂的东西来解决用户角度下本不应该存在的问题。

这些规范可以从隐式转换中借用吗?解决这类问题的常见方法是引入一些表示参数的trait,并从不同的元组到trait的隐式转换。因此,可以将隐式的模糊性规则应用于默认参数。

0
0 Comments

Scala中无法重载两个方法的原因是为了保持生成的字节码在多次编译运行中的稳定性,编译器需要为具有默认参数的方法生成确定性的命名方案。如果在相同的参数位置上有两个带有默认参数的重载方法,需要使用不同的命名方案。解决方法可能是将非默认参数(在方法开头的参数,用于区分重载版本)的类型名称合并到命名方案中。例如,在以下情况下:

def foo(a: String)(b: Int = 42) = a + b
def foo(a: Int)   (b: Int = 42) = a + b

命名方案可能类似于:

def foo$String$default$2 = 42
def foo$Int$default$2 = 42

这样做可以保持字节码稳定性,并解决重载方法命名冲突的问题。然而,这可能会引入一些问题,如当从Java访问Scala方法时,可能会导致接口非常脆弱,用户必须小心处理函数的类型参数变化。此外,对于复杂类型,如`A with B`,该方法可能无法很好地适应。因此,需要进一步的讨论和提出SIP(Scala Improvement Process)提案来解决该问题。

0