2016-08-10 4 views
0

I Zug-MarkerKarte einzigen Typ hList zu hList von Zieltypen

trait TypedTrait { 
    type TYPE 
} 

und die Realisierung

case class TypedString[U](value: String) extends TypedTrait { 
    type TYPE = U 
} 

Und ich möchte Karte HList von String in HList von TypedString gemäß TypedString haben 's Typ Parameter.

Der einfachste Weg ist convert Verfahren zu schaffen (wie in Shapeless map HList depending on target types beschrieben):

val list = "Hello" :: "world" :: HNil 

    val mapped: TypedString[Int] :: TypedString[Boolean] :: HNil = 
      convert[TypedString[Int] :: TypedString[Boolean] :: HNil](list) 

Aber ich möchte redundante Parametrierung vermeiden und so etwas wie folgt verwenden:

val mapped: TypedString[Int] :: TypedString[Boolean] :: HNil = 
       convert[Int :: Boolean :: HNil](list) 

komplette Code Beispiel für die erste Lösung:

 import shapeless._ 

     trait TypedTrait { 
     type TYPE 
     } 

     case class TypedString[U](value: String) extends TypedTrait { 
     type TYPE = U 
     } 

     trait Convert[I <: HList, O <: HList] { def apply(i: I): O } 

     object Convert extends LowPriorityConvertInstances { 
     implicit val convertHNil: Convert[HNil, HNil] = new Convert[HNil, HNil] { 
      def apply(i: HNil): HNil = i 
     } 

     implicit def convertHConsTS[TS, T <: HList, TO <: HList](implicit 
                    c: Convert[T, TO] 
                   ): Convert[String :: T, TypedString[TS] :: TO] = 
      new Convert[String :: T, TypedString[TS] :: TO] { 
      def apply(i: String :: T): TypedString[TS] :: TO = TypedString[TS](i.head) :: c(i.tail) 
      } 
     } 

     sealed class LowPriorityConvertInstances { 
     implicit def convertHCons[H, T <: HList, TO <: HList](implicit 
                   c: Convert[T, TO] 
                  ): Convert[H :: T, H :: TO] = new Convert[H :: T, H :: TO] { 
      def apply(i: H :: T): H :: TO = i.head :: c(i.tail) 
     } 
     } 


     class PartiallyAppliedConvert[O <: HList] { 
     def apply[I <: HList](i: I)(implicit c: Convert[I, O]): O = c(i) 
     } 

     def convert[O <: HList]: PartiallyAppliedConvert[O] = 
     new PartiallyAppliedConvert[O] 

     val list = "Hello" :: "world" :: HNil 

     val mapped: TypedString[Int] :: TypedString[String] :: HNil = 
     convert[TypedString[Int] :: TypedString[String] :: HNil](list) 

Antwort

1

Sie können dies achive indem drei HList Typargumente in Convert:

  • die Art der tatsächlichen HList zu convert weitergegeben (zB String :: String :: HNil)
  • der Typ-Parameter durch den Benutzer vorgeschrieben (zB Int :: Boolean :: HNil)
  • der Ausgangstyp - im Grunde die vorgeschriebenen HList in TypedString gewickelt: zB TypedString[Int] :: TypedString[Boolean] :: HNil.

Der Ausgabetyp vollständig aus dem HList vorgeschriebenen berechnet werden kann, so würde ich die Aux pattern häufig mit shapeless Code verwendet verwenden:

trait Convert[In <: HList, Prescribed <: HList] { 
    type Out <: HList 
    def apply(i: In): Out 
} 

object Convert { 
    type Aux[I <: HList, P <: HList, O <: HList] = Convert[I, P] { type Out = O } 

    // Adapt the implicits accordingly. 
    // The low priority one is left as an exercise to the reader. 

    implicit val convertHNil: Convert.Aux[HNil, HNil, HNil] = 
    new Convert[HNil, HNil] { 
     type Out = HNil 
     def apply(i: HNil): HNil = i 
    } 

    implicit def convertHConsTS[TS, TI <: HList, TP <: HList, TO <: HList](implicit 
    c: Convert.Aux[TI, TP, TO] 
): Convert.Aux[String :: TI, TS :: TP, TypedString[TS] :: TO] = 
    new Convert[String :: TI, TS :: TP] { 
     type Out = TypedString[TS] :: TO 
     def apply(i: String :: TI): TypedString[TS] :: TO = 
     TypedString[TS](i.head) :: c(i.tail) 
    } 
} 

class PartiallyAppliedConvert[P <: HList] { 
    def apply[I <: HList](i: I)(implicit c: Convert[I, P]): c.Out = c(i) 
} 

def convert[O <: HList]: PartiallyAppliedConvert[O] = 
    new PartiallyAppliedConvert[O] 

val list = "Hello" :: "world" :: HNil 

val mapped = convert[Int :: String :: HNil](list) 

Ergebnis:

scala> mapped 
res3: shapeless.::[com.Main.TypedString[Int],shapeless.::[com.Main.TypedString[String],shapeless.HNil]] = TypedString(Hello) :: TypedString(world) :: HNil 

Ich glaube, dass es möglich sein kann, dies unter Verwendung einiger mitbereitgestellten Operationen zu erreichen(shapeless.ops.hlist.Mapped, shapeless.ops.hlist.HKernel, oder shapeless.ops.hlist.RightFolder passen), aber ich weiß nicht, wie man eine Poly Funktion schreibt, die ein type Argument und ein normales Argument nimmt. Irgendwelche Tipps wären willkommen.

+0

Vielen Dank, es ist genau das, was ich brauche. –

Verwandte Themen