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

37

List[T] forSome {type T}List[T forSome {type T}]有什么区别?如何用英语阅读它们?我应该如何理解forSome关键字?forSome的一些实际用途是什么?除了简单的T forSome {type T}用法外,还有哪些有用的实用且更复杂的用法?


1
请查看这个SO帖子这个 - S.R.I
1
而且,来自“info”选项卡的此SO帖子回答了更一般的“什么是存在类型?”问题。 - S.R.I
我认为理解它的唯一方法(而不是花费数周阅读文章)是编写一个带有关键字的方法,尝试实现并使用它。这可能不是最好的方法,但在我的情况下,我认为没有更好的方法了。 - Jacek Laskowski
2个回答

27

注意:(更新于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将为您提供一个未知类型TList。它将很乐意接受这个列表,并与之一起工作。但它不会告诉您T是什么。但您至少知道,列表中的所有元素都是相同类型的T

第二个稍微有点棘手。再次地,API将向您提供一个List。它将使用一些类型T,而不告诉您T是什么。但它可以为每个元素选择不同的类型。真实的API会为T建立一些约束条件,以便它可以实际上使用列表的元素。

结论

当你编写一个API时,每个对象都代表API的实现,此时forSome很有用。每个实现将为您提供一些对象,并接受这些对象返回。但您既不能混合来自不同实现的对象,也不能自己创建对象。相反,您必须始终使用相应的API函数获取一些可与该API一起使用的对象。forSome启用了一种非常严格的封装方式。您可以这样理解forSome

API合同适用于某些类型。但是您不知道适用于哪些类型。因此,您无法提供自己的类型,也无法创建自己的对象。您必须使用使用forSome的API提供的对象。

这种解释比较口语化,可能在某些角落情况下甚至是错误的。但它应该能帮助您理解这个概念。


26

这里有很多问题,其中大部分都已经在评论中的答案中得到了充分的解答,所以我将回答你更具体的第一个问题。

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的foo列表”。整个列表只有一个T。另一方面,第二个可以解释为“foo列表,其中每个foo都是某种T的”。
换句话说,如果我们有一个列表outer: Outer,我们可以说“存在某种类型T,使得outerT类型的foo列表”,而对于类型Inner的列表,我们只能说“对于列表的每个元素,都存在某个T,使得该元素是T类型的foo”。后者更弱——它告诉我们关于列表的信息更少。
例如,如果我们有以下两个列表:
val inner: Inner = List(new Foo[Char], new Foo[Int])
val outer: Outer = List(new Foo[Char], new Foo[Int])

第一个代码可以成功编译,因为列表中的每个元素都是Foo[T]类型的,其中T是某种类型。第二个代码无法编译,因为没有一种类型T能够使得列表中的每个元素都是Foo[T]类型的。


问题是关于理解的。无论如何,我最近注意到规范中2.7.1版本的更改日志附录显示了如何处理List[List[_]]的修复。总之,当一个有话要说的人发表一些言论时,感觉很好。 - som-snytt
使用路径相关类型可以避免存在量化,它将把 forSome {type X} 替换为 forSome {object x} - ayvango

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接