针对联合类型的模式匹配无法从考虑中删除案例

问题描述 投票:0回答:1

假设我的类型是字符串或字符串元组。

type OneOrTwo = String | (String, String)

现在我想区分这两种类型。运行时表示是不同的(

String
Tuple
),所以我应该能够做到这一点。

def getFirst(x: OneOrTwo): String =
  x match {
    // Tuple case
    case (a, _) => a
    // String case
    case y => y
  }

奇怪的是,这不起作用。元组模式是无可辩驳的(对于元组),所以如果该模式失败,那么根据我的理解,在第二种情况下

y
应该是
String
类型。但 Scala 报告
y
属于
OneOrTwo
类型,无法从需要
String
的函数返回。

奇怪的是,Scala 似乎理解这种模式是无可辩驳的。如果我们添加

: String
注释

def getFirst(x: OneOrTwo): String =
  x match {
    // Tuple case
    case (a, _) => a
    // String case
    case y: String => y
  }

然后代码编译成功,我们不会收到非详尽性警告,因为 Scala 知道 every 元组将匹配 Case 1,every 字符串将匹配 Case 2。

那么为什么我们需要这个注释呢?为什么 Scala 不能执行推理的最后一步“如果我们没有元组并且类型是

String | (String, String)
,那么它一定是字符串”?

scala pattern-matching union-types scala-3 dotty
1个回答
1
投票

我不同意这一点,这意味着编译器应该进行另一个不可见的类型转换(好像整个类型擦除过程还不够),可能很快会导致以后出现更多不直观的类型检查问题,并且编译器的透明度甚至更低侧面。

联合类型是用泛型实现的,它们会被删除,所以你会得到一个

Object
:联合类型在编译时被删除,直到它们的最小上限(LUB),应该是
Object 
,因为这两种类型除了
Serializable
之外没有任何共同点,但根据线性化规则应该选择类别而不是特征。

因此,让程序员有责任在模式匹配中处理显式类型转换,可以为您提供更多的灵活性和能力,让您的代码更容易在您的队友以及您回来后的眼睛中进行推理使用相同的代码一段时间后,忘记了该联合在第二种情况下会产生什么结果。

事实上,在这个例子中,编译器可以相当容易地做到这一点。它这样做是因为它向您显示警告。但这是它真正起作用的一种特殊情况。好处并没有超过新创建的问题:我仍然需要仔细检查类型成员以查看联合中的其他类型是什么,只是为了找出第二种情况下模式匹配的类型应该是什么。

请注意,这些规则是在联合类型上精确执行的:

如果模式匹配的选择器是联合类型,则匹配为 如果涵盖了联盟的所有部分,则视为详尽无遗。

动机似乎与推断定义的结果类型(

val
var
def
)时的动机相同,并且我们要推断的类型是联合类型,然后我们将其替换为它的加入,因为推断“过于精确”的类型可能会导致稍后出现不直观的类型检查问题。

© www.soinside.com 2019 - 2024. All rights reserved.