Search code examples
scalanamed-parameters

Confusing resolution of a named argument


A following code is giving me an error with Scala 2.11.7 which is confusing to me:

class A(val a: String, val bVal: Option[String] = None) {
  val b: String = bVal.getOrElse("")

  def copy(a: String = a, b: Option[String] = Some(b)) = new A(a, b)
}

The IntelliJ IDE is not showing any error, but on compile I get an error:

Error:(4, 52) type mismatch;
 found   : Option[String]
 required: String
  def copy(a: String = a, b: Option[String] = Some(b)) = new A(a, b)
                                                   ^

For comparison, this compiles fine:

class A(val a: String, val bVal: Option[String] = None) {
  val b = bVal
  def copy(a: String = a, b: Option[String] = Some(b.getOrElse(""))) = new A(a, b)
}

When I replace Some(b) with Some(this.b), the error goes away, but it is still confusing to me why the error is there in first place. It looks like the compiler is resolving the b in the Some as the parameter of copy, not the b member of A. If this is the case, how can the second version compile without error?


Solution

  • This has got to be a bug. The scope of the default expression includes previous param lists.

    scala> def x = 1
    x: Int
    
    scala> def f(x: Int = x) = 2 * x
    f: (x: Int)Int
    
    scala> f()
    res0: Int = 2
    

    and -Xprint:typer shows the default is correct:

        class A extends scala.AnyRef {
          <paramaccessor> private[this] val a: String = _;
          <stable> <accessor> <paramaccessor> def a: String = A.this.a;
          <paramaccessor> private[this] val bVal: Option[String] = _;
          <stable> <accessor> <paramaccessor> def bVal: Option[String] = A.this.bVal;
          def <init>(a: String, bVal: Option[String] = scala.None): A = {
            A.super.<init>();
            ()
          };
          private[this] val b: String = A.this.bVal.getOrElse[String]("");
          <stable> <accessor> def b: String = A.this.b;
          def copy(a: String = a, b: Option[String] = scala.Some.apply[A](<b: error>)): A = new $iw.this.A(a, b);
          <synthetic> def copy$default$1: String = A.this.a;
          <synthetic> def copy$default$2: Option[String] = scala.Some.apply[String](A.this.b)
        };
        <synthetic> object A extends AnyRef {
          def <init>(): A.type = {
            A.super.<init>();
            ()
          };
          <synthetic> def <init>$default$2: Option[String] = scala.None
        }
    

    Yup.

    scala> def y = "hi"
    y: String
    
    scala> def g(x: String)(y: Option[String] = Some(x)) = y map (_ * 2)
    g: (x: String)(y: Option[String])Option[String]
    
    scala> g("bye")()
    res0: Option[String] = Some(byebye)
    
    scala> def g(x: String)(y: Option[String] = Some(y)) = y map (_ * 2)
    <console>:11: error: type mismatch;
     found   : Option[String]
     required: String
           def g(x: String)(y: Option[String] = Some(y)) = y map (_ * 2)
                                                     ^
    

    Edit: the fact that the default method is correct doesn't mean much, since there are puzzlers related to the fact that the default methods are not very hygienic. E.g., http://scalapuzzlers.com/#pzzlr-051