从 Slick HLIST 中获取元素(或将 Slick HLIst 转换为 Shapeless HList)

Getting elements from Slick HLIST (or Convert Slick HLIst into Shapeless HList)

我使用 slick codegen 自动生成了 scala 代码。我看到一些表行被实现为 HLists。 (但这些是光滑的 HList,而不是普通的无形 HList)

现在我想要 HList 中的一个特定元素作为一行由灵活查询返回。

我用谷歌搜索并找到了这个帖子

但这不适用于灵活的 HList。它适用于 Shapeless HList

apply方法我也试过了

val x : Long = slickHList(2)

但这不会编译,因为 Any 类型不符合 Long 的预期类型。我不想做 .asInstanceOf

有没有一种类型安全的方法可以让我访问灵活的 HList 的元素?

编辑:根据下面的输入我写了下面的代码

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

上面代码的问题在于,从val item = container.get(1)获得的item的类型是at.Out,而不是我预期的Long

build.sbt

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

我还看到两个编译器错误

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)

可以创建扩展方法:

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

示例:

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

希望对您有所帮助。

编辑:这是类型级别的解决方案。

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)

}

示例:

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
}