所以我为它们制作了一些实用程序类和隐式转换。但是,它在从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])
好的,我们发现了问题。确实是因为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
,它们都是Seq
和Set
的超级特征,而是协变(虽然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
}
答案 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>