如何标准化联合类型 (T | Option[T])?

How to normalise a Union Type (T | Option[T])?

我有以下 case class:

case class Example[T](
    obj: Option[T] | T = None,
)

这让我可以像 Example(myObject) 那样构造它,而不是 Example(Some(myObject))

要使用 obj,我需要将其规范化为 Option[T]:

  lazy val maybeIn = obj match
    case o: Option[T] => o
    case o: T => Some(o)

the type test for Option[T] cannot be checked at runtime

我尝试使用 TypeTest 但我也收到警告 - 或者我发现的解决方案看起来非常复杂 - 请参阅

有没有更好的方法在 Scala 3 中实现这种模式?

我不知道 Scala3。但你可以简单地这样做:

case class Example[T](v: Option[T] = None)

object Example {
 def apply[T](t: T): Example[T] = Example(Some(t))
}

编辑:以下答案不正确。从 Scala 3.1 开始,流分析只能检查可空性。有关更多信息,请访问 Scala book.


我认为已经给出的答案可能更适合您提出的用例(公开一个 API 可以采用一个简单的值并将其规范化为一个 Option)。

不过,标题中的问题还是很有意思,我觉得有必要解决一下。

您所观察到的是类型参数在运行时被擦除的结果,即它们仅在编译期间存在,而匹配发生在运行时,一旦它们被已删除.

但是,Scala 编译器能够对联合类型执行流分析。直觉上我会说可能有一种方法可以让它在模式匹配中工作(就像你所做的那样),但是你可以使用 ifisInstanceOf 确保它工作(不那么干净,我同意):

case class Example[T](
    obj: Option[T] | T = None
) {
  lazy val maybeIn =
    if (obj.isInstanceOf[Option[_]]) {
      obj
    } else {
      Some(obj)
    }
}

您可以尝试使用此代码 here on Scastie

Here 是 2019 年将流分析添加到编译器时的公告。

我们有一个 specialized Option-like type for this purpose: OptArg(在 Scala 2 中,但应该很容易移植到 3)

import com.avsystem.commons._

def gimmeLotsOfParams(
  intParam: OptArg[Int] = OptArg.Empty,
  strParam: OptArg[String] = OptArg.Empty
): Unit = ???

gimmeLotsOfParams(42)
gimmeLotsOfParams(strParam = "foo")

它依赖于隐式转换,因此您必须谨慎使用它,即不要将它用作 Option.

的直接替代品

OptArg 的实现非常简单,如果您不需要外部依赖项,那么您可以将它复制到您的项目或某种“公共”库中。