在Scala泛型语法中放置“forSome”子句的重要意义是什么?

时间:2014-05-01 18:34:44

标签: scala existential-type

基于this question的答案,似乎在类型定义的组件之后放置“forSome”与将其放在整个事件的末尾不同。例如,以下内容似乎存在差异:

def one: Foo[U >: T] forSome {type U >: T}

def one: Foo[U forSome {type U >: T}]

Scala语言规范似乎没有说明差异,我想如果将量词移动到外部将没有任何区别。如果它确实有所作为,我认为它将如this answer中所述,其基本上说Set [X forSome {type X}]允许X在set元素之间变化,其中Set [X] forSome {类型X}没有。但是,这似乎不是整个故事和/或不正确,因为这不会编译:

trait Bar {
   def test: Set[X] forSome {type X}
}

def test(b: Bar) {
  val set = b.test
  val h = set.head
  set.contains(h)
}

但这样做:

trait Bar {
   def test: Set[X forSome {type X}]
}

def test(b: Bar) {
  val set = b.test
  val h = set.head
  set.contains(h)
}

似乎Set [X] forSome {type X}为实例化类中的每个使用站点创建一个单独的抽象类型,其中Set [X forSome {type X}]仅创建一个并将其用于整个类。这与我的预期相反,似乎与上面的答案参考不一致。

1 个答案:

答案 0 :(得分:4)

开头的一些观察结果:X forSome { type X }只是一种写作Any的奇特方式 - 它是我们不知道的类型,所以它必须位于类型的顶部层次结构。如果您不相信我,请询问编译器:

scala> implicitly[Any =:= X forSome { type X }]
res0: =:=[Any, _] = <function1>
是的,它同意。

相关地,以下内容将无法编译:

scala> val xs: Set[X forSome { type X }] = Set[Int](1, 2, 3)
<console>:7: error: type mismatch;
 found   : scala.collection.immutable.Set[Int]
 required: Set[X forSome { type X }]
Note: Int <: X forSome { type X }, but trait Set is invariant in type A.

鉴于我们刚刚学到的东西,这并不奇怪。 Set在其类型参数中不变,因此Set[Int]不是Set[X forSome { type X }](即Set[Any])。

考虑到所有这一点,第二个test方法编译也就不足为奇了。当我们走b.test的头时,我们得到X forSome { type X }(即Any),我们需要X forSome { type X }(即Anyb.test.contains

现在是第一个Bar。请考虑以下事项:

scala> val xs: Set[X] forSome { type X } = Set[Int](1, 2, 3)
xs: Set[_] = Set(1, 2, 3)

我们已经说xs是一组特定类型的X,但我们会立即忘记X的所有内容。请注意,与上面的xs定义不同,这确实是编译的,因为我们并不是说xs是一组任何东西,只是它是一组我们不知道的特定类型(或者而不是编译器不知道。)

这意味着绝对没有a可以使xs.contains(a)编译。让我们尝试一个明显的:

scala> xs.contains(1)
<console>:9: error: type mismatch;
 found   : Int(1)
 required: X
              xs.contains(1)
                          ^

这里的错误信息很有趣 - 我们知道X实际上是Int,但编译器没有,因为我们明确要求它忘记这个事实forSome { type X }。通过为第一个test重写Bar方法,您可以看到类似有趣的消息,如下所示:

def test(b: Bar) = b.test.contains(b.test.head)

这也将无法编译,并显示以下消息:

found   : (some other)X(in method test) where type (some other)X(in method test)
required: X(in method test) where type X(in method test)
      def test(b: Bar) = b.test.contains(b.test.head)
                                             ^

即使我们只是从b.test.head中提取了b.test,我们仍然无法将b.test.contains应用于它。我们已经告诉编译器它对b.test的项类型唯一了解的是它存在,所以它不会跟踪b.test.head是我们的事情这一事实应该可以将b.test.contains应用于。