我有类似的东西:
trait Node[P <: Node[_]]
class RootNode extends Node[Null] {
val refB : NodeB[RootNode] = ....
}
class NodeB[P <: Node[_]] extends Node[P] {
val refC : NodeC[NodeB[P]] = ....
}
class NodeC[P <: Node[_]] extends Node[P] {
val refD : NodeD[NodeC[P]] = ....
}
有没有更好的方法来处理这种结构?不知何故,通过我的方法,我们可以使用P
仅约束到直接父级,但是我们已经丢失了父级的父级(依此类推),因此约束不会那么紧张。它可能是。如果不想放弃所有上下文,我将不得不将其更改为:
class NodeC[P <: Node[_]] extends Node[P] {
val refD : NodeD[NodeC[NodeB[NodeA[RootNode]]] = ....
}
这是完全不可行的。
我尝试的任何方法都会导致我进行非法的循环引用。有没有解决这个问题的方法?
答案 0 :(得分:1)
您是否考虑将限制表示为不同类型的结构?这种嵌套确实是可能的,但是看起来你可以使用HList
来实现这种关注。
不是以这种方式表示引用,为什么不通过使用类似下面的内容简单地实现Node
。我在这里提供了一些关于超简单无形模式可以做些什么的通用例子。
如果你可以更具体地了解这些要求,我相信这里的许多人可以提供更多帮助,我的直觉告诉我,围绕HList
的一种更简单的方法可以解决你的问题,而不会有任何讨厌的类型杂耍。
import shapeless._
import shapeless.ops.hlist._
import shapeless.::
class Node[P <: Hlist](hl: P) {
def append[T](obj: T): Node[P :: T] = new Node[P :: T](hl :: obj)
// Much like a normal List, HList will prepend by default.
// Meaning you need to reverse to get the input order.
def reverse[Out]()(
implicit rev: Reverse.Aux[P, Out]
): Out = rev(hl)
// you can enforce type restrictions with equality evidence.
// For instance you can use this to build a chain
// and then make sure the input type matches the user input type.
def equalsFancy[V1 <: Product, Rev, Out <: Product](v1: V1)(
// We inverse the type of the HList to destructure it
// and get the initial order.
implicit rev: Reverse.Aux[P, Rev],
// then convert it to a tuple for example.
tp: Tupler.Aux[Rev, Out],
ev: V1 =:= Out
): Boolean = tp(hl) == v1
}
object Node {
def apply: Node[HNil] = new Node[HNil]
Node().append[String]("test").append[Int](5).equalsFancy("test" -> 5)
}
通过使用Node
,可以很容易地将列表中的type元素限制为LUBConstraint
的子类型。(类型的下限)。
class NodeList[HL <: HList](list: Node[_] :: HL)(implicit val c: LUBConstraint[HL, Node[_])
这意味着您不能再将_ <:< Node[_]
的元素附加到NodeList
,这可能会为您提供一些Poly
个细节。
trait A
trait B
object printPoly extends Poly1 {
// Let's assume these are your A, B and Cs
// You can use Poly to define type specific behaviour.
implicit def caseNodeA[N <: Node[A]] = at[N](node => println("This is an A node"))
implicit def caseNodeB[N <: Node[B]] = at[N](node => println("This is a B node"))
implicit def unknown[N <: Node[_]] = at[N](node => println("This is not known to us yet"))
}
val nodeList: NodeList[..] = ..
nodeList.list.map(printPoly)
<强>更新强>
然后值得实现树状结构。
case class Node[A, F <: HList](value: A, children: F) {
def addChild[T, FF <: HList](
child: Node[T, FF]
): Node[A, HTree[T, FF] :: F] = {
new Node(value, child :: children)
}
def values = Node.Values(this)
}
object Node {
def apply[A](label: A) = new Node[A, HNil](label, HNil)
object Values extends Poly1 {
implicit def caseHTree[A, F <: HList, M <: HList](
implicit fm: FlatMapper.Aux[getLabels.type, F, M],
prepend: Prepend[A :: HNil, M]
): Case.Aux[HTree[A, F], prepend.Out] =
at[HTree[A, F]](tree => prepend(
tree.value :: HNil,
fm(tree.children))
)
}
}