更高的kinded类型链式,可能吗?

时间:2016-07-27 18:54:38

标签: scala generics higher-kinded-types cyclic-reference

我有类似的东西:

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]]] = ....
}

这是完全不可行的。

我尝试的任何方法都会导致我进行非法的循环引用。有没有解决这个问题的方法?

1 个答案:

答案 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))
          )
    }
  }