混淆命名参数的解析

时间:2015-07-22 13:15:17

标签: scala named-parameters

以下代码给我一个Scala 2.11.7错误,这让我感到困惑:

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)
}

IntelliJ IDE没有显示任何错误,但在编译时出现错误:

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

为了比较,这编译得很好:

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)
}

当我用Some(b)替换Some(this.b)时,错误就会消失,但我仍然会混淆为什么错误存在于首位。看起来编译器正在解析b中的Some作为copy的参数,而不是b的{​​{1}}成员。如果是这种情况,第二个版本如何编译而没有错误?

2 个答案:

答案 0 :(得分:1)

这必须是一个错误。默认表达式的范围包括先前的参数列表。

scala> def x = 1
x: Int

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

scala> f()
res0: Int = 2

-Xprint:typer显示默认值是正确的:

    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
    }

烨。

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)
                                                 ^

编辑:默认方法正确的事实并不重要,因为有一些与默认方法不太卫生相关的益智游戏。例如,http://scalapuzzlers.com/#pzzlr-051

答案 1 :(得分:-1)

您的理解是正确的,当存在与周围类型成员同名的参数时,编译器将首选该参数。

在您的非编译版本中,b的类型为Option[String]。这意味着Some(b)的类型为Option[Option[String]],它与A的构造函数的第二个参数的类型不匹配。

在第二个编译版本中,b仍为Option[String]类型。但是,b.getOrElse("")的类型为String。这意味着Some(b.getOrElse(""))的类型Option[String]A的第二个构造函数参数有效。