什么是何时使用Scala的forSome关键字?

时间:2014-01-20 03:55:29

标签: scala types existential-type

List[T] forSome {type T}List[T forSome {type T}]之间有什么区别?我如何用“英语”阅读它们?我应该如何 grok forSome关键字? forSome有哪些实际用途?什么是实用的,比简单的T forSome {type T}用法更复杂?

2 个答案:

答案 0 :(得分:25)

这里有很多问题,其中大部分已经在上述评论中链接的答案中得到了相当深入的解答,所以我会回答你更具体的第一个问题。

List[T] forSome { type T }List[T forSome { type T }]之间没有真正有意义的区别,但我们可以看到以下两种类型之间的差异:

class Foo[A]

type Outer = List[Foo[T]] forSome { type T }
type Inner = List[Foo[T] forSome { type T }]

我们可以将第一个读作“T的foos列表,对于某些类型T”。整个列表中只有一个T。另一方面,第二个可以被理解为“一个foos列表,其中每个foo对于某些T来说都是T。”

换句话说,如果我们有一个列表outer: Outer,我们可以说“存在某种类型TouterT的列表{1}}“,对于类型Inner的列表,我们只能说”对于列表的每个元素,存在一些T,使得该元素是{{1}的foo }}”。后者较弱 - 它告诉我们较少的清单。

因此,例如,如果我们有以下两个列表:

T

第一个将编译得很好 - 列表的每个元素对于某些val inner: Inner = List(new Foo[Char], new Foo[Int]) val outer: Outer = List(new Foo[Char], new Foo[Int]) Foo[T]。第二个不会编译,因为没有一些T,因此列表的每个元素都是T

答案 1 :(得分:21)

注意:更新2016-12-08 )根据Martin Odersky关于ScalaX 2016的演讲,forSome关键字很可能会消失Scala 2.13或2.14将其替换为路径依赖类型或匿名类型属性(A[_])。在大多数情况下这是可能的。如果您的边缘情况不可能,请重构您的代码或放宽您的类型限制。

如何阅读“forSome”(以非正式方式)

通常当您使用通用API时,API会保证您可以使用您提供的任何类型(最多一些给定的约束)。因此,当您使用List[T]时,List API会向您保证它可以与您提供的任何类型T 一起使用。

使用forSome(所谓的存在量化类型参数),反之亦然。 API 将提供一种类型(不是你)并且它保证你,它将使用它为你提供的这种类型。语义是,具体对象将为您提供T类型的东西。同一个对象也会接受它为您提供的内容。但是没有其他对象可以使用这些T,并且没有其他对象可以为您提供T类型的内容。

“存在量化”的概念是:存在(至少)一种类型T(在实现中)以履行API的合同。但我不会告诉你它是哪种类型。

forSome可以类似地读取:对于某些类型T,API合同适用。但对于所有类型T都不一定如此。因此,当您提供某种类型T(而不是API实现中隐藏的类型)时,编译器无法保证您获得了正确的T。所以它会抛出一个类型错误。

应用于您的示例

因此,当您在API中看到List[T] forSome {type T}时,您可以这样阅读:API将为您提供某种未知类型List的{​​{1}}。它很乐意接受这个列表,它将与它一起使用。但它不会告诉你,T是什么。但至少你知道,列表中的所有元素都属于同一类型T

第二个有点棘手。 API再次为您提供T。并且它将使用某种类型List而不会告诉您T是什么。但是可以自由地为每个元素选择不同的类型。真实世界的API会为T建立一些约束,因此它实际上可以使用列表的元素。

结论

当您编写API时,

T非常有用,其中每个对象代表API的实现。每个实现都将为您提供一些对象,并将接受这些对象。但是你既不能混合不同实现的对象,也不能自己创建对象。相反,您必须始终使用相应的API函数来获取可与该API一起使用的一些对象。 forSome支持非常严格的封装。您可以通过以下方式阅读forSome

  

对于某些类型,API合约折叠为true。但你不知道   哪种类型适用。因此,您无法提供自己的类型和   你不能创建自己的对象。你必须使用提供的   通过使用forSome

的API

这是非常非正式的,在某些极端情况下甚至可能是错误的。但它应该帮助你理解这个概念。