2017-09-15 4 views
1

J'ai généré un code scala en utilisant slick codegen. Je vois que certaines tables Rows sont implémentées en tant que HLists. (mais ce sont des HList lisses, pas les HList sans forme normale)Récupérer des éléments de Slick HLIST (ou Convertir Slick HLIst en HList Shapeless)

Maintenant je veux qu'un élément spécifique de la liste HList soit retourné comme une ligne par la requête slick.

Je googlé et trouvé ce fil

Getting elements from an HList

Mais cela ne fonctionne pas pour HList lisse. il fonctionne très bien pour Shapeless HList

J'ai aussi essayé la méthode apply

val x: Long = slickHList (2)

mais cela ne compile pas, car le type Tout ne se conforme pas le type de exected Longue. Je détesterais faire un .asInstanceOf

Existe-t-il une manière de typesafe dans laquelle je peux accéder aux éléments du HList lisse?

Edit: Basé sur l'entrée ci-dessous j'ai écrit le code ci-dessous

package com.abhi 

object SlickAndShapeless { 
    import slick.collection.heterogeneous.{HCons, HList, HNil} 
    import slick.collection.heterogeneous.syntax.HNil 

    type MyRow = HCons[Long, HCons[String, HNil]] 
    val row : MyRow = 1L :: "foo" :: HNil 
    import HListExtensions._ 
    val hlist = row.asShapeless 
    val container = new Container(hlist) 
    val item = container.get(1) 
} 

class Container[L <: shapeless.HList](list: L) { 
    import shapeless._ 
    import nat._ 
    import ops.hlist._ 
    def get(n: Nat)(implicit at: At[L, n.N]): at.Out = list[n.N] 
} 

object HListExtensions { 
    import slick.collection.heterogeneous.{HNil => SHNil, HList => SHList, HCons} 
    import shapeless.{::, HList, HNil} 

    implicit class HListShapelessSlick(val list: HList) extends AnyVal { 
     def asSlick : SHList = list match { 
     case HNil => SHNil 
     case head :: tail => head :: tail.asSlick 
     } 
    } 

    implicit class HListSlickShapeless(val list: SHList) extends AnyVal { 
     def asShapeless : HList = list match { 
     case SHNil => HNil 
     case HCons(head, tail) => head :: tail.asShapeless 
     } 
    } 
} 

Le problème avec le code ci-dessus est que le type de item obtenu à partir val item = container.get(1) est at.Out et non Long comme je m'y attendais.

build.sbt

libraryDependencies ++= Seq(
    "com.typesafe.slick" % "slick_2.12" % "3.2.1", 
    "com.chuusai" % "shapeless_2.12" % "2.3.2" 
) 

Je vois aussi deux erreurs du compilateur

Error:(19, 35) Implicit not found: shapeless.Ops.At[shapeless.HList, shapeless.Succ[shapeless._0]]. You requested to access an element at the position shapeless.Succ[shapeless._0], but the HList shapeless.HList is too short. 
    val item : Long = container.get(1) 
Error:(19, 35) not enough arguments for method get: (implicit at: shapeless.ops.hlist.At[shapeless.HList,shapeless.Succ[shapeless._0]])at.Out. 
Unspecified value parameter at. 
    val item : Long = container.get(1) 

Répondre

3

Il est possible de créer des méthodes d'extension:

object HListExtensions { 

    import slick.collection.heterogeneous.{HNil => SHNil, HList => SHList, HCons} 
    import shapeless.{ ::, HList, HNil } 

    implicit class HListShapelessSlick(val list:HList) extends AnyVal { 
    def asSlick:SHList = list match { 
     case HNil => SHNil 
     case head :: tail => head :: tail.asSlick 
    } 
    } 

    implicit class HListSlickShapeless(val list:SHList) extends AnyVal { 
    def asShapeless:HList = list match { 
     case SHNil => HNil 
     case HCons(head, tail) => head :: tail.asShapeless 
    } 
    } 
} 

Exemple:

scala>import HListExtensions._ 
import HListExtensions._ 

scala> val x1:HList = 1 :: 2 :: HNil 
x1: slick.collection.heterogeneous.HList = 1 :: 2 :: HNil 

scala> x1.asShapeless 
res1: shapeless.HList = 1 :: 2 :: HNil 

scala> x1.asShapeless.asSlick 
res2: slick.collection.heterogeneous.HList = 1 :: 2 :: HNil 

J'espère que cela aide.

Editer: Voici une solution au niveau du type.

object HListsConvertersTypeLevel { 

    import shapeless.{::} 

    sealed trait HConv[From <: heterogeneous.HList, To <: shapeless.HList] { 
    def convert(list: From): To 
    } 

    implicit def buildHConvNil: HConv[heterogeneous.HNil.type, shapeless.HNil] = 
    new HConv[heterogeneous.HNil.type, shapeless.HNil] { 
     override def convert(list: heterogeneous.HNil.type): shapeless.HNil = shapeless.HNil 
    } 

    implicit def buildHConv[H, T <: heterogeneous.HList, T2 <: shapeless.HList](
     implicit conv: HConv[T, T2]): HConv[HCons[H, T], ::[H, T2]] = new HConv[HCons[H, T], ::[H, T2]] { 

    override def convert(list: HCons[H, T]): ::[H, T2] = { 
     list.head :: conv.convert(list.tail) 
    } 
    } 

    def toShapeless[A <: heterogeneous.HList, B <: shapeless.HList](list: A)(implicit conv: HConv[A, B]): B = conv.convert(list) 

} 

Exemple:

object SlickAndShapeless { 
    import slick.collection.heterogeneous.{HCons, HNil} 
    import slick.collection.heterogeneous.syntax.HNil 

    type MyRow = HCons[Long, HCons[String, HNil]] 
    val row: MyRow = 1L :: "foo" :: HNil 

    import HListsConvertersTypeLevel._ 
    val hlist   = toShapeless(row) 
    val item: Long = hlist.head 
    val item2: String = hlist.tail.head 
} 
+0

Merci ce qui est excellent. J'ai combiné votre solution avec l'autre lien. mais j'ai toujours un problème. J'ai mis à jour ma question ci-dessus. –

+0

Le problème est que sur le hlist shapeless si je fais un 'container.get (1)' le type de retour est de type 'at.Out' et non Long –