【问题标题】:Getting elements from Slick HLIST (or Convert Slick HLIst into Shapeless HList)从 Slick HLIST 获取元素(或将 Slick HLIst 转换为 Shapeless HList)
【发布时间】:2017-09-15 16:03:39
【问题描述】:

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

现在我希望 HList 中的特定元素通过 slick 查询作为 Row 返回。

我google了一下,找到了这个帖子

Getting elements from an HList

但这不适用于光滑的 HList。它非常适用于无形 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)

【问题讨论】:

    标签: scala slick shapeless


    【解决方案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
    }
    

    【讨论】:

    • 谢谢你,太好了。我将您的解决方案与其他链接结合在一起。但我还有一个问题。我已经更新了我上面的问题。
    • 问题是,如果我执行container.get(1),那么在无形 hlist 上,返回类型是 at.Out 类型而不是 Long
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多