【问题标题】:What is the significant of the placement of the "forSome" clause in Scala generics syntax?在 Scala 泛型语法中放置“forSome”子句有什么意义?
【发布时间】:2014-05-02 09:01:06
【问题描述】:

根据对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[X] forSome {type 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}] 仅创建一个并将其用于整个类.这与我的预期相反,并且似乎与上面的答案参考不一致。

【问题讨论】:

  • 这至少应该澄清一点stackoverflow.com/questions/21226004/…
  • @Andrey - 这很有趣,特别是因为该问题的公认答案暗示语义与我所看到的相反。
  • @Andrey - 实际上我会更进一步,并说该问题的公认答案作为这个问题的答案没有用,并且可能完全错误。请参阅上面我修改后的示例。
  • this page 上的倒数第二个代码块解释了差异。我认为您的第一个是 All Sets,第二个是 A Set for all Whats

标签: scala existential-type


【解决方案1】:

首先要注意几点: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 }(即Any)来表示b.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 }明确要求它忘记这个事实.通过为第一个 Bar 重写 test 方法,您可以看到类似的有趣消息,如下所示:

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.headb.test 中提取出来,我们仍然无法将 b.test.contains 应用于它。我们告诉编译器它唯一知道b.test 的项目类型是它存在,所以它不会跟踪b.test.head 是我们应该能够做到的事情的事实申请b.test.contains

【讨论】:

  • 谢谢,这很有道理。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-08
  • 1970-01-01
  • 2012-07-06
  • 1970-01-01
  • 2016-05-26
  • 2011-03-07
相关资源
最近更新 更多