【问题标题】:How to normalise a Union Type (T | Option[T])?如何规范联合类型(T | Option[T])?
【发布时间】:2021-12-23 18:14:35
【问题描述】:

我有以下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,但也收到警告 - 或者我发现的解决方案看起来非常复杂 - 请参阅 https://stackoverflow.com/a/69608091/2750966

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

【问题讨论】:

  • Option[T] | T 类型没有意义。您想要的实际上是一种在创建Example 时从T 创建Some[T] 的设施方法,而不是在使用它时进行一些统一。请参阅@ziggystar 的以下回答。
  • 您无法匹配 Option[T],因为这不是具体类型。如果您认为这是您想要的,您可以尝试Option[_]。 T 类型是抽象的,所以你也不能匹配/instanceOf ......你可以这样做 o => .... 所以最好重新考虑你的设计选择。
  • 如果T=Option[A] 会发生什么?甚至T = Option[Option[B]] ?
  • @LuisMiguelMejíaSuárez,谢谢你的回答很好地解释了它。您提到“也许联合类型的引入可以打开一种更简单的方式来编码可选参数的可能性?”这个问题正是关于这个 - 联合类型是否有一个很好的模式。那么你有答案了吗?

标签: scala scala-3


【解决方案1】:

我不了解 Scala3。但你可以简单地这样做:

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

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

【讨论】:

  • 是的,如果只有一个参数,那就可以了——但我最多有 10 个——这会给你很多apply 函数。
  • @pme 是的,但是你可以避免模式匹配的问题。您只需要处理一个简单的选项。 :)
  • 考虑最常见的 API 用法:您不需要提供所有可能的 apply 变体。
【解决方案2】:

我们有一个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 的实现非常简单,如果您不想要外部依赖项,那么您可以将其复制到您的项目或某种“公共”库中。

【讨论】:

    【解决方案3】:

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


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

    但是,标题中的问题仍然很有趣,我认为解决它是有意义的。

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

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

    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 年在编译器中添加流分析的公告。

    【讨论】:

    • 好吧,如果你输入Some(obj: T) 它不会编译。 :D 所以我认为流量分析不会在这里发挥作用。
    • 我认为这最好地回答了问题的标题——甚至社区认为联合类型不是解决选项模式的好方法。
    • 我仔细看了一下,确实maybeIn 是Any 类型。查看文档,看起来流分析仅适用于可空性检查。我写了一些 TypeScript,我期待更复杂的东西。我的错。我将编辑答案以确保人们不会得出错误的结论。
    猜你喜欢
    • 1970-01-01
    • 2019-05-23
    • 1970-01-01
    • 2016-11-08
    • 2018-05-09
    • 2015-05-03
    • 2011-01-05
    • 2014-03-24
    • 1970-01-01
    相关资源
    最近更新 更多