gpt4 book ai didi

scala - `T {}` 在 Scala 中做什么

转载 作者:行者123 更新时间:2023-12-03 07:19:06 25 4
gpt4 key购买 nike

浏览 Shapeless 代码时,我发现了这个看似无关的 {} herehere :

trait Witness extends Serializable {
type T
val value: T {}
}

trait SingletonOps {
import record._
type T
def narrow: T {} = witness.value
}

我几乎忽略了它作为一个拼写错误,因为它什么也没做,但显然它做了一些事情。请参阅此提交:https://github.com/milessabin/shapeless/commit/56a3de48094e691d56a937ccf461d808de391961

我不知道它是做什么的。有人可以解释一下吗?

最佳答案

任何类型后面都可以跟有 {} 封闭的类型序列和抽象非类型成员定义。这称为“细化”,用于为正在细化的基本类型提供额外的精度。在实践中,细化最常用于表达对被细化类型的抽象类型成员的约束。

一个鲜为人知的事实是,这个序列允许为空,在无形源代码中可以看到的形式中,T {} 是类型 T 带有空的细化。任何空的精炼都是...空的...因此不会向精炼类型添加任何额外的约束,因此类型 TT {} 是等效的。我们可以让 Scala 编译器为我们验证这一点,就像这样,

scala> implicitly[Int =:= Int {}]
res0: =:=[Int,Int] = <function1>

那么我为什么要在无形中做这种看似毫无意义的事情呢?这是因为细化的存在和类型推断之间的相互作用。如果你查看the relevant section在 Scala 语言规范中,您将看到类型推断算法至少在某些情况下尝试避免推断单例类型。这是一个这样做的例子,

scala> class Foo ; val foo = new Foo
defined class Foo
foo: Foo = Foo@8bd1b6a

scala> val f1 = foo
f1: Foo = Foo@8bd1b6a

scala> val f2: foo.type = foo
f2: foo.type = Foo@8bd1b6a

正如您从 f2 的定义中看到的那样,Scala 编译器知道值 foo 具有更精确的类型 foo.type (即 val foo 的单例类型),但是,除非明确请求,否则它不会推断出更精确的类型。相反,它推断出非单例(即加宽)类型 Foo,正如您在 f1 的情况中看到的那样。

但在无形状的 Witness 情况下,我明确希望推断单例类型以使用 value 成员(整个Witness 的要点是使我们能够通过单例类型在类型和值级别之间传递),那么有什么方法可以说服 Scala 编译器这样做吗?

事实证明,空的细化确实可以做到这一点,

scala> def narrow[T <: AnyRef](t: T): t.type = t
narrow: [T <: AnyRef](t: T)t.type

scala> val s1 = narrow("foo") // Widened
s1: String = foo

scala> def narrow[T <: AnyRef](t: T): t.type {} = t // Note empty refinement
narrow: [T <: AnyRef](t: T)t.type

scala> val s2 = narrow("foo") // Not widened
s2: String("foo") = foo

正如您在上面的 REPL 记录中所看到的,在第一种情况下,s1 已被输入为扩展类型 String,而 s2 则为已分配单例类型 String("foo")

这是 SLS 强制要求的吗?不,但它是一致的,并且有一定的道理。 Scala 的大部分类型推断机制都是实现定义的,而不是规范的,这可能是最不令人惊讶和最不成问题的实例之一。

关于scala - `T {}` 在 Scala 中做什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35903100/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com