简体   繁体   English

更高种类的链式链,可能吗?

[英]Higher kinded type chained, possible?

I have something like: 我有类似的东西:

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

Is there a better way to deal with this sort of structure? 有没有更好的方法来处理这种结构? Somehow, with my approach, we could use P to constraint only to the immediate parent Level, but we have lost the parent of the parent (and so on), and therefore the constrain wouldn't be all the tight that it could be. 以某种方式,通过我的方法,我们可以使用P来仅约束直接父级,但是我们失去了父级(等等),因此约束不会那么紧。 If do not want to loose all the context I would have to change it to something like: 如果不想丢失所有上下文,则必须将其更改为以下内容:

class NodeC[P <: Node[_]] extends Node[P] {
   val refD : NodeD[NodeC[NodeB[NodeA[RootNode]]] = ....
}

which is completely infeasible. 这是完全不可行的。

Any approach that I have tried has lead me to an illegal cyclic reference. 我尝试过的任何方法都导致我使用了非法的循环引用。 Is there any solution to this problem? 有什么解决办法吗?

Have you considered representing the restriction as a different kind of structure? 您是否考虑过将限制表示为另一种结构? That kind of nesting is indeed possible, however it looks like the kind of concern you could implement using HList . 确实可以进行这种嵌套,但是看起来您可以使用HList实现这种关注。

Instead of representing refs in that fashion, why not simply your implementation of Node by using something like the following. 与其以这种方式表示引用,不如通过使用类似以下内容的方法简单地实现Node的实现。 I'm offering some generic examples here of what could be done with super simple shapeless patterns. 我在这里提供一些通用示例,说明可以使用超简单的无形模式完成的工作。

If you can be more specific about the requirements, I'm sure many here can do more to help, my gut tells me there's a simpler approach around HList that can answer your problem without any nasty type juggling. 如果您可以更具体地了解需求,那么我敢肯定,这里的许多人可以提供更多帮助,我的直觉告诉我HList周围有一种更简单的方法,可以解决您的问题而无需进行任何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)
}

It's quite easy to restrict the type element in your list to only subtype of Node by using an LUBConstraint as well.(lower upper bound for the type). 也可以通过使用LUBConstraint将列表中的type元素限制为仅Node的子类型(类型的下界)。

class NodeList[HL <: HList](list: Node[_] :: HL)(implicit val c: LUBConstraint[HL, Node[_])

This would mean you can no longer append elements that are not _ <:< Node[_] to the NodeList which could give you some Poly niceties. 这意味着您不能再将非_ <:< 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)

Update 更新资料

It's worth implementing a Tree like structure then. 然后值得实现树状结构。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM