[scala] 스칼라 컴파일러가 왜 기본 인수로 오버로드 된 메소드를 허용하지 않습니까?

그러한 메소드 오버로딩이 모호해질 수있는 유효한 경우가있을 수 있지만, 컴파일러는 왜 컴파일 타임이나 런타임에 모호하지 않은 코드를 허용하지 않습니까?

예:

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

// This fails, too. Even if there is no position in the argument list,
// where the types are the same.
def foo(a: Int)   (b: Int = 42) = a + b
def foo(a: String)(b: String = "Foo") = a + b

// This is OK:
def foo(a: String)(b: Int) = a + b
def foo(a: Int)   (b: Int = 42) = a + b

// Even this is OK.
def foo(a: Int)(b: Int) = a + b
def foo(a: Int)(b: String = "Foo") = a + b

val bar = foo(42)_ // This complains obviously ...

이러한 제한을 조금 풀 수없는 이유가 있습니까?

특히 과부하가 걸린 Java 코드를 스칼라 기본 인수로 변환하는 것은 매우 중요하며 스펙 / 컴파일러가 임의의 제한을 부과하는 하나의 스칼라 메소드로 많은 Java 메소드를 대체 한 후에는 찾기가 좋지 않습니다.



답변

Lukas Rytz ( 여기 부터 ) 를 인용하고 싶습니다 .

그 이유는 기본 인수를 반환하는 생성 된 메서드에 대한 결정적 명명 체계를 원했기 때문입니다. 당신이 쓰는 경우

def f(a: Int = 1)

컴파일러는

def f$default$1 = 1

동일한 매개 변수 위치에 기본값으로 두 개의 과부하가있는 경우 다른 이름 지정 체계가 필요합니다. 그러나 여러 컴파일러 실행에서 생성 된 바이트 코드를 안정적으로 유지하려고합니다.

향후 스칼라 버전에 대한 해결책은 기본이 아닌 인수의 유형 이름 (과도한 방법으로 오버로드 된 버전을 명확하게하는 메소드 시작시)을 이름 지정 스키마에 통합하는 것입니다 (예 :이 경우).

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

누군가 SIP 제안서를 기꺼이 작성 하시겠습니까?


답변

오버로드 해상도와 기본 인수의 상호 작용에 대한 읽기 쉽고 정확한 사양을 얻는 것은 매우 어렵습니다. 물론 여기에 제시된 사례와 같은 많은 개별 사례의 경우 어떤 일이 발생해야하는지 쉽게 말할 수 있습니다. 그러나 충분하지 않습니다. 가능한 모든 코너 케이스를 결정하는 사양이 필요합니다. 오버로드 해상도는 이미 지정하기가 매우 어렵습니다. 믹스에 기본 인수를 추가하면 여전히 어려워집니다. 그래서 우리는 둘을 분리하기로 결정했습니다.


답변

귀하의 질문에 대답 할 수 없지만 다음과 같은 해결 방법이 있습니다.

implicit def left2Either[A,B](a:A):Either[A,B] = Left(a)
implicit def right2Either[A,B](b:B):Either[A,B] = Right(b)

def foo(a: Either[Int, String], b: Int = 42) = a match {
  case Left(i) => i + b
  case Right(s) => s + b
}

하나의 arg에서만 다른 두 개의 매우 긴 arg 목록이 있으면 문제가 될 수 있습니다 …


답변

나를 위해 일한 것은 오버로드 방법을 재정의하는 것입니다 (Java 스타일).

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

이를 통해 현재 매개 변수에 따라 원하는 해상도를 컴파일러에 보장 할 수 있습니다.


답변

@ 랜디 답변의 일반화는 다음과 같습니다.

당신이 정말로 원하는 것 :

def pretty(tree: Tree, showFields: Boolean = false): String = // ...
def pretty(tree: List[Tree], showFields: Boolean = false): String = // ...
def pretty(tree: Option[Tree], showFields: Boolean = false): String = // ...

해결 방법

def pretty(input: CanPretty, showFields: Boolean = false): String = {
  input match {
    case TreeCanPretty(tree)       => prettyTree(tree, showFields)
    case ListTreeCanPretty(tree)   => prettyList(tree, showFields)
    case OptionTreeCanPretty(tree) => prettyOption(tree, showFields)
  }
}

sealed trait CanPretty
case class TreeCanPretty(tree: Tree) extends CanPretty
case class ListTreeCanPretty(tree: List[Tree]) extends CanPretty
case class OptionTreeCanPretty(tree: Option[Tree]) extends CanPretty

import scala.language.implicitConversions
implicit def treeCanPretty(tree: Tree): CanPretty = TreeCanPretty(tree)
implicit def listTreeCanPretty(tree: List[Tree]): CanPretty = ListTreeCanPretty(tree)
implicit def optionTreeCanPretty(tree: Option[Tree]): CanPretty = OptionTreeCanPretty(tree)

private def prettyTree(tree: Tree, showFields: Boolean): String = "fun ..."
private def prettyList(tree: List[Tree], showFields: Boolean): String = "fun ..."
private def prettyOption(tree: Option[Tree], showFields: Boolean): String = "fun ..."


답변

가능한 시나리오 중 하나는


  def foo(a: Int)(b: Int = 10)(c: String = "10") = a + b + c
  def foo(a: Int)(b: String = "10")(c: Int = 10) = a + b + c

컴파일러는 어느 것을 호출할지 혼동 될 것입니다. 다른 가능한 위험을 방지하기 위해 컴파일러는 최대 하나의 오버로드 된 메소드가 기본 인수를 갖도록 허용합니다.

그냥 내 추측 🙂


답변

내 이해는 기본 인수 값으로 컴파일 된 클래스에 이름 충돌이있을 수 있다는 것입니다. 여러 줄에서 언급 된이 줄을 따라 뭔가를 보았습니다.

명명 된 인수 사양은 다음과 같습니다.
http://www.scala-lang.org/sites/default/files/sids/rytz/Mon,%202009-11-09,%2017:29/named-args.pdf

상태는 다음과 같습니다.

 Overloading If there are multiple overloaded alternatives of a method, at most one is
 allowed to specify default arguments.

따라서 당분간은 작동하지 않습니다.

Java에서 할 수있는 것과 같은 작업을 수행 할 수 있습니다. 예 :

def foo(a: String)(b: Int) =  a + (if (b > 0) b else 42)