我只是困惑于这样一个事实:当内部不是pf时,这个部分函数MatchError并没有与MatchError一起爆炸。
sealed trait Inner
case class InnerA(name: String) extends Inner
case class InnerB(name: String, value: Int) extends Inner
case class Input(id: String, inner: Inner)
case class Output(id: String, inner: InnerA)
val pf: PartialFunction[Input, Output] = { input =>
input.inner match {
case innerA: InnerA =>
val Input(id, _) = input
Output(id, innerA)
}
}相反,它是简单的未定义的,因此它通过。
Seq(
Input("1", InnerA("a1")),
Input("2", InnerB("b2", 2)),
Input("3", InnerA("a3"))
).collect(pf) shouldBe Seq(
Output("1", InnerA("a1")),
Output("3", InnerA("a3"))
)如果我添加一行,就会收到编译警告,并试图在上面的集合中传递一个InnerB,就会抛出一个MatchError (正如我最初预期的那样):
val pf: PartialFunction[Input, Output] = { input =>
println(input)
input.inner match {
case innerA: InnerA =>
val Input(id, _) = input
Output(id, innerA)
}
}InnerB(b2,2) (of class casa.InnerB)
scala.MatchError: InnerB(b2,2) (of class casa.InnerB)为什么会这样呢?这个怪癖在什么地方被记录下来了吗?
(我使用Scala2.13.3)
发布于 2020-08-21 07:33:37
当内部不是MatchError时,这个部分函数pf不会与InnerA一起爆炸
如果你直接用这样的论点来称呼它
pf(Input("a", InnerB("b", 0)))您确实得到了一个MatchError;但是PartialFunction的全部意义是在isDefinedAt中提供其他信息,而collect使用它时不调用isDefinedAt返回false的pf。
这个怪癖在什么地方被记录下来了吗?
见规范的6.23.1翻译段:
当需要一个
PartialFunction时,会合成一个额外的成员isDefinedAt,它只是返回true。但是,如果函数文字具有形状x => x match { … },那么isDefinedAt是从模式匹配派生出来的,其方式如下:匹配表达式中的每个大小写计算结果为true,如果没有默认大小写,则添加一个默认大小写,其计算结果为false。
因此,对于您的第二个版本,isDefinedAt总是true;第一个版本并不完全适合x => x match...,但显然它也是受支持的。现在,通常定义PartialFunction的方法就像Luisía Suárez的评论那样
{ case Input(id, InnerA(name)) => Output(id, InnerA(name)) }但它只是简单地被翻译成
x => x match { case Input(id, InnerA(name)) => Output(id, InnerA(name)) }发布于 2020-08-20 22:10:47
正在发生的情况是,在第一种情况下,编译器正在“删除”input匹配并使用input.inner。
当我运行scalac -Xprint:typer Test.scala时,第一段代码变成:
final override def applyOrElse[A1 <: Input, B1 >: Output](input: A1, default: A1 => B1): B1 = (input.inner: Inner @unchecked) match {
case (innerA @ (_: InnerA)) => {
val id: String = (input: A1 @unchecked) match {
case (id: String, inner: Inner): Input((id @ _), _) => id
};
Output.apply(id, innerA)
}
case (defaultCase$ @ _) => default.apply(input)
};
final def isDefinedAt(input: Input): Boolean = (input.inner: Inner @unchecked) match {
case (innerA @ (_: InnerA)) => true
case (defaultCase$ @ _) => false
}这意味着您的函数的行为将像PartialFunction[Inner, Output],因此编译器知道它不需要警告您,您的匹配不是详尽无遗的。
另一方面,当您看到带有print指令的方法的结果时,您会得到:
final override def applyOrElse[A1 <: Input, B1 >: Output](input: A1, default: A1 => B1): B1 = ((input.asInstanceOf[Input]: Input): Input @unchecked) match {
case (defaultCase$ @ _) => {
scala.Predef.println("xxx");
input.inner match {
case (innerA @ (_: InnerA)) => {
val id: String = (input: A1 @unchecked) match {
case (id: String, inner: Inner): Input((id @ _), _) => id
};
Output.apply(id, innerA)
}
}
}
case (defaultCase$ @ _) => default.apply(input)
};
final def isDefinedAt(input: Input): Boolean = ((input.asInstanceOf[Input]: Input): Input @unchecked) match {
case (defaultCase$ @ _) => true
case (defaultCase$ @ _) => false
}在本例中,您将创建一个为所有输入间隔定义的PartialFunction[Input, Output],这是很好的。但是,当编译器检查内部input.inner match时,它警告您,这个匹配(不是第一个匹配)并不是详尽无遗的。
https://stackoverflow.com/questions/63510390
复制相似问题