Why does the Scala compiler disallow overloaded methods with default arguments?

后端 未结 7 1711
失恋的感觉
失恋的感觉 2020-11-29 19:26

While there might be valid cases where such method overloadings could become ambiguous, why does the compiler disallow code which is neither ambiguous at compile time nor at

相关标签:
7条回答
  • 2020-11-29 19:43

    Here is a generalization of @Landei answer:

    What you really want:

    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 = // ...
    

    Workarround

    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 ..."
    
    0 讨论(0)
  • 2020-11-29 19:44

    My understanding is that there can be name collisions in the compiled classes with default argument values. I've seen something along these lines mentioned in several threads.

    The named argument spec is here: http://www.scala-lang.org/sites/default/files/sids/rytz/Mon,%202009-11-09,%2017:29/named-args.pdf

    It states:

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

    So, for the time being at any rate, it's not going to work.

    You could do something like what you might do in Java, eg:

    def foo(a: String)(b: Int) =  a + (if (b > 0) b else 42)
    
    0 讨论(0)
  • 2020-11-29 19:47

    I'd like to cite Lukas Rytz (from here):

    The reason is that we wanted a deterministic naming-scheme for the generated methods which return default arguments. If you write

    def f(a: Int = 1)

    the compiler generates

    def f$default$1 = 1

    If you have two overloads with defaults on the same parameter position, we would need a different naming scheme. But we want to keep the generated byte-code stable over multiple compiler runs.

    A solution for future Scala version could be to incorporate type names of the non-default arguments (those at the beginning of a method, which disambiguate overloaded versions) into the naming schema, e.g. in this case:

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

    it would be something like:

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

    Someone willing to write a SIP proposal?

    0 讨论(0)
  • 2020-11-29 19:53

    I can't answer your question, but here is a workaround:

    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
    }
    

    If you have two very long arg lists which differ in only one arg, it might be worth the trouble...

    0 讨论(0)
  • 2020-11-29 19:53

    What worked for me is to redefine (Java-style) the overloading methods.

    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"
    

    This ensures the compiler what resolution you want according to the present parameters.

    0 讨论(0)
  • 2020-11-29 19:57

    One of the possible scenario is

    
      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
    

    The compiler will be confused about which one to call. In prevention of other possible dangers, the compiler would allow at most one overloaded method has default arguments.

    Just my guess:-)

    0 讨论(0)
提交回复
热议问题