隐式定义适用于Seq但不适用于Set

时间:2014-04-24 16:00:36

标签: scala set implicit seq

所以我为它们制作了一些实用程序类和隐式转换。但是,它在从Seq转换而不是从Set转换时工作正常,尽管代码是相同的,并且这两个特征乍一看似乎非常相似。可能是什么问题,我将如何解决它?

import scala.collection.mutable.HashMap

trait Indexed[T] {
  def index: T
}

class IndexMap[T, V <: Indexed[T]] extends HashMap[T, V] {
  override def put(key: T, value: V): Option[V] = {
    require(key == value.index)
    super.put(key, value)
  }
  final def put(value: V): Option[V] = put(value.index, value)
}

trait Named extends Indexed[String] {
  final def index = name
  def name: String
}

type NameMap[T <: Named] = IndexMap[String, Named]

这很好用:

implicit def seq2IndexMap[T, V <: Indexed[T]](s: Seq[V]): IndexMap[T,V] = {
  val ret = new IndexMap[T,V]();
  s.foreach(v => ret.put(v))
  ret
}

但是,无法使用type mismatch; found : scala.collection.immutable.Set[Program.ClassData] required: Common.NameMap[Program.ClassData] (which expands to) Common.IndexMap[String,Common.Named]

进行编译
implicit def set2IndexMap[T, V <: Indexed[T]](s: Set[V]): IndexMap[T,V] = {
  val ret = new IndexMap[T,V]();
  s.foreach(v => ret.put(v))
  ret
}

输入时:

val c = Class("Test", Set(ClassData("data1", null), ClassData("data2", null)))

ClassData扩展Named

我正在使用Scala 2.10。

编辑:

为方便起见,简化了Class和ClassData的定义:

case class ClassData(name: String, p: Any) extends Named
case class Class(n: String, data: NameMap[ClassData])

编辑2:

好的,我们发现了问题。确实是因为Set是不变的(我不明白为什么)。

当我写Set(ClassData("data1", null))时,它创建了一个Set[ClassData],无法解释为Set[Named],而它与Seq一起使用,因为Seq是协变。

有趣的是,当我们明确调用转换时,Scala没有任何问题:

val c = Class("Test", set2IndexMap((Set(ClassData("data1", null), ClassData("data2", null))))

我认为Scala能够在这种情况下推断出要推断的Set类型。在我看来,这表明Scala如何过于复杂。如果我的显式版本也有错误,我可以立即看到隐式转换的错误。我觉得幕后发生了太多事情,最终你必须了解它们,否则你会遇到这样的问题。

解决方案是明确说明集合的类型:

val c = Class("Test", Set[Named](ClassData("data1", null), ClassData("data2", null)))

更好的解决方案是让隐式转换适用于Iterable甚至Traversable,它们都是SeqSet的超级特征,而协变(虽然Set不是,但是协变 Iterable)。

implicit def set2IndexMap[T, V <: Indexed[T]](s: Traversable[V]): IndexMap[T,V] = {
  val ret = new IndexMap[T,V]();
  s.foreach(v => ret.put(v))
  ret
}

1 个答案:

答案 0 :(得分:1)

臭名昭着,Set在其类型参数中是不变的。

这似乎使隐含不适用?​​

可能没有正确推断V。有时它喜欢推断Nothing

(发布一个完整的最小化将有助于某人帮助你。)

当我有机会时,我会尝试破译-Ytyper-debug,但FTR:

继续Seq:

|    |    |    solving for (A: ?A)
|    |    |    |-- seq2IndexMap BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value b  in Test) implicits disabled
|    |    |    |    [adapt] [T, V <: Code.this.Indexed[T]](s: Seq[V])Code.this.IndexM... adapted to [T, V <: Code.this.Indexed[T]](s: Seq[V])Code.this.IndexM...
|    |    |    |    \-> (s: Seq[V])nosetconvert.Test.IndexMap[T,V]
|    |    |    solving for (T: ?T, V: ?V)
|    |    |    [adapt] seq2IndexMap adapted to [T, V <: Code.this.Indexed[T]](s: Seq[V])Code.this.IndexM... based on pt Seq[nosetconvert.Test.ClassData] => nosetconvert.Test.NameMap[nosetconvert.Test.ClassData]
|    |    |    |-- [T, V <: Code.this.Indexed[T]](s: Seq[V])Code.this.IndexM... : pt=nosetconvert.Test.NameMap[nosetconvert.Test.ClassData] BYVALmode-EXPRmode (silent: value b  in Test) implicits disabled
|    |    |    |    \-> nosetconvert.Test.IndexMap[String,nosetconvert.Test.Named]
|    |    |    [adapt] [A](elems: A*)CC[A] adapted to [T, V <: Code.this.Indexed[T]](s: Seq[V])Code.this.IndexM... based on pt nosetconvert.Test.NameMap[nosetconvert.Test.ClassData]
|    |    |    \-> nosetconvert.Test.IndexMap[String,nosetconvert.Test.Named]

设置失败:

|    |    |    solving for (A: ?A)
|    |    |    |-- set2IndexMap BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value c  in Test) implicits disabled
|    |    |    |    [adapt] [T, V <: Code.this.Indexed[T]](s: Set[V])Code.this.IndexM... adapted to [T, V <: Code.this.Indexed[T]](s: Set[V])Code.this.IndexM...
|    |    |    |    \-> (s: Set[V])nosetconvert.Test.IndexMap[T,V]
|    |    |    |-- nosetconvert.this.Test.set2IndexMap BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value c  in Test) implicits disabled
|    |    |    |    [adapt] [T, V <: Code.this.Indexed[T]](s: Set[V])Code.this.IndexM... adapted to [T, V <: Code.this.Indexed[T]](s: Set[V])Code.this.IndexM...
|    |    |    |    \-> (s: Set[V])nosetconvert.Test.IndexMap[T,V]
|    |    |    \-> <error>