Understanding definition and desugaring of "Option" in Scala 3 book

Cli*_*ton 6 scala scala-3

I'm starting a Scala role in a few weeks yet I haven't written any Scala before (yes, my future employers know this), although I've written a lot of C# and Haskell. Anyway I was skimming through the Scala 3 book, and found this example:

enum Option[+T]:
  case Some(x: T)
  case None
Run Code Online (Sandbox Code Playgroud)

Which apparently dusugars into:

enum Option[+T]:
  case Some(x: T) extends Option[T]
  case None       extends Option[Nothing]
Run Code Online (Sandbox Code Playgroud)

My two questions are:

  1. How exactly does the mechanism of this desugaring work? In particular why does Some by default extend Option[T] whereas None extends Option[Nothing]?
  2. This seems like a strange way to define Option. I would define it like this:
enum Option[+T]:
  case Some(x: T) extends Option[T]
  case None       extends Option[T]
Run Code Online (Sandbox Code Playgroud)

Indeed with None extending Option[None] wouldn't this fail?

Option[string] x = None;
Run Code Online (Sandbox Code Playgroud)

As Option[T] is covariant in T and None is not a subtype of string?

I'm missing something quite fundamental here I'm sure.

Sil*_*olo 5

关于枚举脱糖工作原理的一个很好的来源是Issue #1970的原始提案。

该页面上与您的问题相关的部分标题为“脱糖”。我们来看看你的Option定义。

enum Option[+T]:
  case Some(x: T)
  case None
Run Code Online (Sandbox Code Playgroud)

Some是带有类型参数的枚举下的参数化情况,因此规则#4适用

如果 E 是具有类型参数 Ts 的枚举类,则其伴生对象中的 case 不带 extends 子句

case C <params> <body>
Run Code Online (Sandbox Code Playgroud)

...

对于 C 没有类型参数的情况,假设 E 的类型参数是

V1 T1 > L1 <: U1 ,   ... ,    Vn Tn >: Ln <: Un      (n > 0)
Run Code Online (Sandbox Code Playgroud)

其中每个方差 Vi 是“+”或“-”。然后案件扩大到

case C <params> extends E[B1, ..., Bn] <body>
Run Code Online (Sandbox Code Playgroud)

因此,正如您所期望的,您的Some案例有一个extends条款。Option[T]然后规则#5 为我们提供了实际的案例类别。

另一方面,None出于同样的原因,您的使用类型参数,因此结果基于方差。

  • 如果T是不变的,我们必须明确指定一个extends子句
  • 如果T是协变的,我们得到Nothing
  • 如果T是逆变的,我们得到Any

T是协变的,所以我们扩展,记住它是for any 的Option[Nothing]子类型。因此,你的赋值(记住,在 Scala 中,我们使用/来声明变量,而不仅仅是类型名称)Option[S] Svalvar

val x: Option[String] = None;
Run Code Online (Sandbox Code Playgroud)

None是类型 的值None.type,它扩展了Option[Nothing]. Option[Nothing]是 的子类型Option[String]因为Nothing是 的子类型String。这样任务就成功了。

Nil这与扩展(空列表)的原因相同List[Nothing]。我可以构造一个Nil具有具体类型( 的子类型List[Nothing])的 ,然后我可以在它前面添加我想要的任何内容。结果列表不再是List[Nothing]; 1 +: Nil是 aList[Int]"a" +: Nil是 a List[String],但重点是,它Nil可以来自我的程序中的任何位置,我们不必预先决定我们希望它是什么类型。我们不能(安全地)在可变数据类型上使用协方差,所以这一切都有效,因为ListOption是不可变的。可变集合的ArrayBuffer类型参数是不变的。(注意:Java 的内置数组是协变的,这是不健全的并且会导致各种问题)