2016-07-27 2 views
0

私が持っているようなもの:チェーン付きのより親切な型式が可能ですか?

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

制限を別の種類の構造として表すと考えましたか?そのようなネスティングは確かに可能ですが、HListを使用して実装できる懸念のようです。

このようにしてrefsを表現するのではなく、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) 
} 

それだけでなくLUBConstraintを使用してNodeのサブタイプのみにあなたのリスト内のタイプの要素を制限することは非常に簡単です。(タイプの下限上限)。

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

これはもはやあなたにいくつかのPoly細かな点を与えることができるNodeListから_ <:< Node[_]ない要素を追加することはできませんを意味します。

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

お寄せいただきありがとうございます。私はすでにラッパーのようなことを考えていましたが、リストに似ているわけではありませんが、ツリーのように(最終的には同じ)、すべてのノードは他のツリーエントリのコンテナになります...しかし、要素自体にアクセスするためには、それは少し複雑なように聞こえます。また、私は私のポストで最初に提案したような親レベルに到達することは、要件のために完全にうまくいっていますが、100%理想的ではないことを認めなければなりません。それ以上のラッパー。 – lqbweb

関連する問題