命名参数的混淆解析
Confusing resolution of a named argument
下面的代码给我一个 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(this.b)
替换 Some(b)
时,错误消失了,但我仍然很困惑为什么错误首先出现。看起来编译器正在将 Some
中的 b
解析为 copy
的参数,而不是 A
的 b
成员。如果是这样,第二个版本怎么编译不出错?
您的理解是正确的,当存在与周围类型成员同名的参数时,编译器将优先选择该参数。
在您的非编译版本中,b
的类型为 Option[String]
。这意味着 Some(b)
的类型 Option[Option[String]]
与 A
的构造函数的第二个参数的类型不匹配。
在第二次编译版本中,b
仍然是Option[String]
类型。但是,b.getOrElse("")
是 String
类型。这意味着 Some(b.getOrElse(""))
的类型 Option[String]
对 A
.
的第二个构造函数参数有效
这一定是一个错误。默认表达式的范围包括以前的参数列表。
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: String = A.this.a;
<synthetic> def copy$default: 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: 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
下面的代码给我一个 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(this.b)
替换 Some(b)
时,错误消失了,但我仍然很困惑为什么错误首先出现。看起来编译器正在将 Some
中的 b
解析为 copy
的参数,而不是 A
的 b
成员。如果是这样,第二个版本怎么编译不出错?
您的理解是正确的,当存在与周围类型成员同名的参数时,编译器将优先选择该参数。
在您的非编译版本中,b
的类型为 Option[String]
。这意味着 Some(b)
的类型 Option[Option[String]]
与 A
的构造函数的第二个参数的类型不匹配。
在第二次编译版本中,b
仍然是Option[String]
类型。但是,b.getOrElse("")
是 String
类型。这意味着 Some(b.getOrElse(""))
的类型 Option[String]
对 A
.
这一定是一个错误。默认表达式的范围包括以前的参数列表。
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: String = A.this.a;
<synthetic> def copy$default: 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: 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