假设我希望Item
的每个实例都是SourceOf
Item
,而SourceOf
的每个Item
子类型都是SourceOf
1}}该子类型的所有实例。这似乎是一种自然的方式:
object Demo {
trait Item extends SourceOf[this.type]
trait SourceOf[+A <: Item]
}
显然这不起作用,因为this
引用Demo
对象,而不引用每个Item
实例。 (我已经测试过了。)
如何告诉Scala编译器每个Item
都是自己的源?应该可以让编译器推导出类似的东西:
“(所有)农具的每一个来源都是您正在寻找的特定农具的来源。”
“如果您正在寻找农具的来源,并且您已经拥有农具,那么您不需要再看了。”
这是一个例子(为简洁起见忽略类型擦除):
trait FarmingImplement extends Item
object TheRototiller extends FarmingImplement
def findSource(item: Item, seq: Seq[SourceOf[_]]): Option[SourceOf[item.type]] =
seq.collectFirst {
case src: SourceOf[item.type] => src
}
val sources = Seq( // example: possible sources of TheRototiller
new SourceOf[Nothing] { override def toString = "Wrong source" },
new SourceOf[FarmingImplement] { override def toString = "Right" },
TheRototiller /* also right */ )
val got = findSource(TheRototiller, sources).get
println(got) // Should print "Right", since the second source in `sources`
// is the first match.
我希望got
的类型为SourceOf[TheRototiller.type]
,而不是SourceOf[Item]
。但主要是我希望Scala的类型系统能够确定SourceOf
是否匹配给定Item
或Items
类别。
答案 0 :(得分:1)
至少,这将通过传递this
来解决您的问题:
object Demo {
trait C[+A]
trait Item extends SourceOf {
type T = C[this.type]
}
trait SourceOf {
type T <: C[Item]
}
}
示例(您必须在此处比较T
而不是SourceOf
):
scala> val i = new Item{}
i: Demo.Item = $anon$1@c70cb4c
scala> implicitly[i.T =:= i.T] //compile-time type equality check
res4: =:=[i.T,i.T] = <function1>
scala> val i2 = new Item{}
i2: Demo.Item = $anon$1@1f26ac06
scala> implicitly[i.T =:= i2.T]
<console>:18: error: Cannot prove that i.T =:= i2.T.
implicitly[i.T =:= i2.T]
如果你需要在运行时检查类型(正如你在模式匹配的示例中所示) - 我建议使用TypeTag
:
import scala.reflect.runtime.universe._
object Demo {
abstract class C[+A: TypeTag]
trait Item extends SourceOf {
type T = C[this.type]
}
trait SourceOf {
val tag = typeTag[this.type] //just to access
}
}
用法:
scala> val i = new Item{}
i: Demo.Item = $anon$1@3b2111b0
scala> val i2 = new Item{}
i2: Demo.Item = $anon$1@4e7bb48a
scala> typeTag[i.T].tpe =:= typeTag[i2.T].tpe //runtime type-equality check
res9: Boolean = false
scala> typeTag[i.T].tpe =:= typeTag[i.T].tpe
res10: Boolean = true
最后,实施findSource
:
import scala.reflect.runtime.universe._
object Demo {
trait Item extends SourceOf
trait SourceOf {
type I = this.type
val tag = typeTag[I]
}
def findSource(item: Item, seq: Seq[SourceOf]): Option[item.I] =
seq.find(_.tag.tpe =:= item.tag.tpe).asInstanceOf[Option[item.I]]
}
示例:
scala> val i = new Item{}
i: Demo.Item = $anon$1@1fc2899d
scala> val i2 = new Item{}
i2: Demo.Item = $anon$1@5f308abb
scala> val ri = findSource(i, Seq(i, i2)).get
ri: i.I = $anon$1@1fc2899d
scala> implicitly[ri.I =:= i.I]
res2: =:=[ri.I,i.I] = <function1>
使用 asInstanceOf
(这里安全可行),因为Seq
在编译时失去了路径依赖类型,我们使用运行时检查来匹配类型,所以没办法找到它的编译器。但是,Shapeless2的基于HList
+ Selector
的实现可能更纯粹。