2017-01-13 12 views
2

シェイプレスtypeclassメソッド内で再帰呼び出しを実装する正しい方法は何ですか?シェイプレス:typeclassの再帰呼び出し

(早期警告:私は型崩れ学んでいるので、私はまだ知らない明白な答え/選択肢があるかもしれないすべてのヘルプは大歓迎です。!)

私はにケースクラスを変換型クラスを持っています潜在的に再帰的なMapを返す代わりに、潜在的に再帰的なメンバーで構成されたケースクラスを返すこと以外は、this stackoverflow questionに記載されているToMapRecの例に似ています。代わりにMyTypeのインスタンス変換のそう:moreを作成するには

case class ReturnType(name: String, data: Option[Any], more: Set[ReturnType]) 

:(他の質問のように)ネストされた/おそらく再帰Map[String,Any]

trait GetsConverted 
case class MyType(someData: String, one: GetsConverted, other: GetsConverted) extends GetsConverted 
case class MyOtherType(blah: AndSoOn) extends GetsConverted 
case class AndSoOn(eventualFinalValue: Int) 

を、それがのインスタンスのようなものを返します。メンバーは型クラスの中で再帰呼び出しを行う必要があるようです。しかし、別のメソッドの内部で型クラスの変換メソッドを呼び出すには、その関数内のすべての型の暗黙のパラメータを最外側の呼び出しにスレッドする必要があります。だからではなく、型クラスの変換等:

implicit def hconsToMapRec0[K, V, A <: HList, B <: HList, W, C <: HList, D <: HList, X, E <: HList, F <: HList](implicit 
    wit: Witness.Aux[K], 
    gen0: LabelledGeneric.Aux[V, A], 
    tmrH0: Lazy[ToMapRec[A]], 
    tmrT0: Lazy[ToMapRec[B]], 
    gen1: LabelledGeneric.Aux[W, C], 
    tmrH1: Lazy[ToMapRec[C]], 
    tmrT1: Lazy[ToMapRec[D]], 
    gen2: LabelledGeneric.Aux[X, E], 
    tmrH2: Lazy[ToMapRec[E]], 
    tmrT2: Lazy[ToMapRec[F]] 
): ReturnType = ??? 

または多分より悪い:

implicit def hconsToMapRec0[K, V, A <: HList, B <: HList](implicit 
    wit: Witness.Aux[K], 
    gen: LabelledGeneric.Aux[V, R], 
    tmrH: Lazy[ToMapRec[A]], 
    tmrT: Lazy[ToMapRec[B]] 
): ReturnType = ??? 

深さ3つの方法(私は推測している)のような関数のシグネチャ何かを必要とします。一般に、このアプローチでは、この再帰で深さレベルを掛けた暗黙のパラメータを持つメソッドが必要になります。深さのレベルの数は、実行時にのみ認識されます。だからこそこれを行う方法ではありません。

これは、スカラーコレクションライブラリのハードコーディングされた22-アリティメソッドに似ています。私が今までに学んだShapeless-fooを求める問題のように思えます。

そこで質問です:私は近くに何かを得るために管理

ReturnType("MyType", Some("someData"), Set(
    ReturnType("MyOtherType", None, Set(
    ReturnType("AndSoOn", Some(10), Set()) 
)), 
    ReturnType("MyOtherType", None, Set(
    ReturnType("AndSoOn", Some(20), Set()) 
)) 
)) 

答えて

1

:どのようにあなたは次のように再帰的に定義された値に上記MyTypeの例のようなものを構造化、任意のケースクラスを変換するために、型崩れ型クラスを記述します以下の実装例を参考にしてください。 そのほとんどはthisの質問に対する回答に似ています。違いはReturnTypeに変換され、Coproductケース(これはsealed traitの総称表現です)の場合もいくつか追加されています。

だから、次のコードで:

val gen = LabelledGeneric[GetsConverted] 
val m = MyType("someData", MyOtherType(AndSoOn(10)), MyOtherType(AndSoOn(20))) 
val tmr = ToReturnTypeRec[gen.Repr] 
val returnType = tmpr(gen.to(m)) 

あなたはここで結果に

ReturnType(MyType,Some(someData),Set(
    ReturnType(MyOtherType,None,Set(
     ReturnType(,Some(20),Set()) 
    )), 
    ReturnType(MyOtherType,None,Set(
     ReturnType(,Some(10),Set()) 
    )) 
)) 

を取得するには、実装です。このため

trait ToReturnTypeRec[L] { def apply(l: L): ReturnType } 

trait LowPriorityToReturnTypeRec { 
implicit def hconsToReturnTypeRec1[K <: Symbol, V, T <: HList](implicit 
    wit: Witness.Aux[K], 
    tmrT: ToReturnTypeRec[T] 
): ToReturnTypeRec[FieldType[K, V] :: T] = new ToReturnTypeRec[FieldType[K, V] :: T] { 
    def apply(l: FieldType[K, V] :: T): ReturnType = 
     tmrT(l.tail) match { 
     case ReturnType(n,d,m) => ReturnType("", Some(l.head), m) 
     } 
    } 
} 

object ToReturnTypeRec extends LowPriorityToReturnTypeRec { 
    def apply[T](implicit tmr: ToReturnTypeRec[T]) = tmr 
    implicit val hnilToReturnTypeRec: ToReturnTypeRec[HNil] = new ToReturnTypeRec[HNil] { 
    def apply(l: HNil): ReturnType = ReturnType("", None, Set()) 
    } 

    implicit def hconsToReturnTypeRec0[K <: Symbol, V, T <: HList, R](implicit 
    // wit: Witness.Aux[K], 
    gen: LabelledGeneric.Aux[V, R], 
    tmrH: Lazy[ToReturnTypeRec[R]], 
    tmrT: ToReturnTypeRec[T] 
): ToReturnTypeRec[FieldType[K, V] :: T] = new ToReturnTypeRec[FieldType[K, V] :: T] { 
    def apply(l: FieldType[K, V] :: T): ReturnType = 
     tmrT(l.tail) match { 
     case ReturnType(n,d,m) => ReturnType(n, d, m + tmrH.value(gen.to(l.head))) 
     } 
    } 

    implicit val cnillToReturnTypeRec: ToReturnTypeRec[CNil] = new ToReturnTypeRec[CNil] { 
    def apply(c: CNil): ReturnType = ReturnType("Impossible", None, Set()) 
    } 

    implicit def cconsToReturnTypeRec0[K <: Symbol, V <: Product, T <: Coproduct](implicit 
    wit: Witness.Aux[K], 
    tmrH: Lazy[ToReturnTypeRec[V]], 
    tmrT: Lazy[ToReturnTypeRec[T]] 
): ToReturnTypeRec[FieldType[K,V] :+: T] = new ToReturnTypeRec[FieldType[K, V] :+: T] { 
    def apply(c: FieldType[K,V] :+: T): ReturnType = { 
     c match { 
     case Inl(h) => tmrH.value(h) match { 
      case ReturnType(_,d,m) => ReturnType(wit.value.name, d, m) 
     } 
     case Inr(t) => tmrT.value(t) 
     } 
    } 
    } 

    implicit def genericToReturnTypeRec[P, R](implicit 
    gen: LabelledGeneric.Aux[P, R], 
    tmr: Lazy[ToReturnTypeRec[R]] 
): ToReturnTypeRec[P] = new ToReturnTypeRec[P] { 
    def apply(p: P): ReturnType = tmr.value(gen.to(p)) 
    } 
} 
+0

ありがとう! +1の努力をしていましたが、おそらく元のクエーストーンで再帰が必要な場所を強調表示するのはうまくいきませんでした。これは、両方ともリンクしている同じ問題でかなりよく定義されています。しかし、問題は、コールサイトが別の(再帰的な)機能の中にあるときに起こります。これを一度行うことは簡単ですが、あなたはどのように再帰的に呼び出しますか? 'val returnType = tmpr(gen.to(m))'? – Ryan

関連する問題