2017-06-15 27 views
4

因此,scala编译器抱怨方式foo的模式匹配可能并不完整,我不知道为什么。这是代码:匹配的部分可能并不完整无误警告不正确

abstract class Foo { 
    def foo(that: Foo): Unit = (this, that) match { 
     case (Foo_1(), Foo_1()) => //case 1 
     case (Foo_1(), Foo_2()) => //case 2 
     case (Foo_2(), Foo_1()) => //case 3 
     case (Foo_2(), Foo_2()) => //case 4 
      // Compiler warning 
    } 

    def fooThis(): Unit = this match { 
     case Foo_1() => //do something 
     case Foo_2() => //do something 
      // Works fine 
    } 

    def fooThat(that: Foo): Unit = that match { 
     case Foo_1() => //do something 
     case Foo_2() => //do something 
      // Works fine 
    } 
} 
case class Foo_1() extends Foo 
case class Foo_2() extends Foo 

这是错误:

Warning:(5, 32) match may not be exhaustive. 
It would fail on the following inputs: (Foo(), _), (Foo_1(), _), (Foo_2(), _), (_, Foo()), (_, Foo_1()), (_, Foo_2()), (_, _) 
    def foo(that: Foo): Unit = (this, that) match { 

由于thisthatFoo类型,Foo只能是Foo_1类型或Foo_2的,在foo的案件所有可能的组合。

为了完整起见,我添加了fooThisfooThat,并表明匹配Foo_1Foo_2就足够了。编译器消息表明还有其他类型可以匹配(即Foo_)。

那么为什么会显示此警告?

相关:


编辑

编译器似乎只要你使用的元组抱怨。如果我们添加一个虚拟变量来fooThis如下

def fooThis(): Unit = (this, Foo_1()) match { 
    case (Foo_1(),_) => //do something 
    case (Foo_2(),_) => //do something 
} 

我们得到以下编译器警告

Warning:(13, 27) match may not be exhaustive. 
It would fail on the following input: (_, _) 
    def fooThis(): Unit = (this, Foo_1()) match { 

回答

4

Scala编译器不会给详尽的匹配警告,非密封特性(如您的Foo)。这解释了为什么fooThisfooThat编译没有警告。

如果你想在这里警告(你应该,因为他们是在运行时比MatchError例外),你有两个选择:

  1. Foo密封。这会创建一个ADT,这对于模式匹配是安全的,因为在您忘记案例时您会收到详尽警告。 Option是您可能从标准库中熟悉的ADT。在这里,您已经有Foo_1Foo_2的案例,所以您不会收到详尽警告。但是,如果你忘记了任何一种情况,你会的。您可能想在制作时确定Foo_1Foo_2
  2. 请假Foo解除密封,使用Typelevel Scala并启用其​​警告。

在另一方面,Scala编译器将详尽匹配警告,最后一种情况类,如Tuple2,这是你对你的foo方法相匹配的内容。

要回答“为什么所示的警告?”,考虑一下,如果我们这样做会发生什么:

case class Foo3() extends Foo 
val foo3 = Foo3() 
foo3.foo(foo3) 

(答案:它抛出在运行时MatchError

警告是斯卡拉编译器帮助您在运行时避免异常的方式。如果你想警告消失,你可以:

  1. Foo密封(再次,创建ADT),防止Foo3在别处鬼鬼祟祟。
  2. 添加通配符case _ => ...
  3. 使比赛取消选中:((this, that): @unchecked) match { ...

不要做3号,因为它让你容易受到MatchError在运行期,当有人介绍Foo3

所以,也许问题是不是真正的“为什么在比赛中foo发出警示”,而是“为什么比赛中fooThisfooThat生成一个警告”。

+0

我确实可能会问我的问题是错的,我宁愿问你最后一段的问题。我错过了什么,或者你没有在这个答案中自己回答吗?对我来说,这种不一致困扰着我,而不是编译器产生警告。 – Didii

+0

你是对的,不一致是震动。 (不满意的)答案最终是Scala编译器不会为未密封的特征生成穷举警告。你并不是唯一想要的,正如Typelevel的fork中的'strict-unsealed-patmat'编译器选项所证明的那样。也许我们会在未来的某个Lightbend斯卡拉的版本得到严格'开封-patmat'。在此期间我猜只是意识到这一点,并坚持对抽象数据类型匹配。 – danielnixon

1

这似乎使密封抽象类至少使编译器警告消失:

sealed abstract class Foo { 

虽然我不太清楚为什么。这可能涉及到:https://issues.scala-lang.org/browse/SI-9351

+0

感谢您的信息。将问题添加到“相关”列表。 – Didii

+0

这是因为密封'Foo'排除了一些假设的'Foo3'的存在,它也扩展了'Foo',满足了编译器的要求,事实上这个匹配是详尽无遗的。 – danielnixon

0

搞清楚一类被称为类层次结构分析的所有子类,并与动态代码加载一个语言做静态CHA相当于解决停机问题。另外,Scala的目标之一是独立编译和部署独立模块,因此编译器无法知道某个类是否在另一个模块中被子类化,因为它从不会查看多个模块。 (毕竟,您可以在没有该模块的情况下针对其他模块的接口编译模块)这就是为什么密封要求在同一编译单元中定义所有子类。 这就是为什么编译器不显示警告,因为它知道现有的子类。

相关问题