Alexi Alexi - 13 days ago 7
Scala Question

Trying to make a HCons List CaseClassShape

Okay, so I want to create a HListCaseClassShape that will let me create case classes that overcome the 22 arity limit. So, starting from Stefan Zeiger's code here.

final class HListShape[Level <: ShapeLevel, M <: HList, U <: HList : ClassTag, P <: HList](val shapes: Seq[Shape[_, _, _, _]]) extends MappedScalaProductShape[Level, HList, M, U, P] {
def buildValue(elems: IndexedSeq[Any]) = elems.foldRight(HNil: HList)(_ :: _)
def copy(shapes: Seq[Shape[_ <: ShapeLevel, _, _, _]]) = new HListShape(shapes)
}
implicit def hnilShape[Level <: ShapeLevel] = new HListShape[Level, HNil.type, HNil.type, HNil.type](Nil)
implicit def hconsShape[Level <: ShapeLevel, M1, M2 <: HList, U1, U2 <: HList, P1, P2 <: HList](implicit s1: Shape[_ <: Level, M1, U1, P1], s2: HListShape[_ <: Level, M2, U2, P2]) =
new HListShape[Level, M1 :: M2, U1 :: U2, P1 :: P2](s1 +: s2.shapes)


Then I go on to create my HListCaseClassShape:

class HListCaseClassShape[P <: Product, LiftedList, LiftedCaseClass <: P, PlainList, PlainCaseClass <: P](
mapLifted: LiftedList => LiftedCaseClass, mapPlain: PlainList => PlainCaseClass)(
implicit columnShapes: Shape[FlatShapeLevel, LiftedList, PlainList, LiftedList], classTag: ClassTag[PlainCaseClass])
extends MappedScalaProductShape[FlatShapeLevel, P, LiftedCaseClass, PlainCaseClass, LiftedCaseClass] {
val shapes = columnShapes.asInstanceOf[HListShape[_,_,_,_]].shapes
override def toMapped(v: Any) = {
val folded = v.asInstanceOf[Product].productIterator.foldRight(HNil: HList)(_ :: _)
folded.asInstanceOf[PlainList]
}
def buildValue(elems: IndexedSeq[Any]) = {
val list = elems.foldRight(HNil: HList)(_ :: _)
mapLifted(list.asInstanceOf[LiftedList])
}
def copy(s: Seq[Shape[_ <: ShapeLevel, _, _, _]]) = new HListCaseClassShape(mapLifted, mapPlain) { override val shapes = s }
}


And finally, I try to instantiate it

case class LiftedB(data1: Rep[String], data2: Rep[String], data3: Rep[String], data4: Rep[String], data5: Rep[String], data6: Rep[String], data7: Rep[String], data8: Rep[String], data9: Rep[String], data10: Rep[String], data11: Rep[String], data12: Rep[String], data13: Rep[String], data14: Rep[String], data15: Rep[String], data16: Rep[String], data17: Rep[String], data18: Rep[String], data19: Rep[String], data20: Rep[String], data21: Rep[String], data22: Rep[String], data23: Rep[String], data24: Rep[String], data25: Rep[String])
case class B(data1: String, data2: String, data3: String, data4: String, data5: String, data6: String, data7: String, data8: String, data9: String, data10: String, data11: String, data12: String, data13: String, data14: String, data15: String, data16: String, data17: String, data18: String, data19: String, data20: String, data21: String, data22: String, data23: String, data24: String, data25: String )


... and then the shape:

type DirectConsList = HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HCons[String, HNil]]]]]]]]]]]]]]]]]]]]]]]]]
type LiftedConsList = HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HCons[Rep[String], HNil]]]]]]]]]]]]]]]]]]]]]]]]]

implicit object BShape extends HListCaseClassShape[
Product,
LiftedConsList,
LiftedB,
DirectConsList,
B](toLifted, toDirect)

def toLifted(list: LiftedConsList) = {
list match {
case data1 :: data2 :: data3 :: data4 :: data5 :: data6 :: data7 :: data8 :: data9 :: data10 :: data11 :: data12 :: data13 :: data14 :: data15 :: data16 :: data17 :: data18 :: data19 :: data20 :: data21 :: data22 :: data23 :: data24 :: data25 :: HNil => {
LiftedB(data1, data2, data3, data4, data5, data6, data7, data8, data9, data10, data11, data12, data13, data14, data15, data16, data17, data18, data19, data20, data21, data22, data23, data24, data25 )
}
case _ => throw new Exception("malformed HList")
}
}

def toDirect(list:DirectConsList) = {
list match {
case data1 :: data2 :: data3 :: data4 :: data5 :: data6 :: data7 :: data8 :: data9 :: data10 :: data11 :: data12 :: data13 :: data14 :: data15 :: data16 :: data17 :: data18 :: data19 :: data20 :: data21 :: data22 :: data23 :: data24 :: data25 :: HNil => {
B(data1, data2, data3, data4, data5, data6, data7, data8, data9, data10, data11, data12, data13, data14, data15, data16, data17, data18, data19, data20, data21, data22, data23, data24, data25 )
}
case _ => throw new Exception("malformed HList")
}
}


Only when I instantiate and run via db.stream it throws an exception:

Exception in thread "main" java.lang.ClassCastException: slick.collection.heterogeneous.HCons cannot be cast to com.example.CaseClassShapeQueryExample$B
at com.example.CaseClassShapeQueryExample$$anonfun$2.apply(CaseClassShapeQueryExample.scala:106)
at slick.backend.DatabasePublisher$$anon$3$$anonfun$onNext$1.apply(DatabasePublisher.scala:48)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)


So it's got the plain tuple object (i.e.
DirectConsList
) coming back from the query but I can't cast it into the
B
object. What am I doing wrong???

Answer

Looks like all I was missing was a mapPlain(folded.asInstanceOf[PlainList]) in the toMapped method. Working HListCaseClassShape implementation is as follows:

class HListCaseClassShape[P <: Product, LiftedList, LiftedCaseClass <: P, PlainList, PlainCaseClass <: P](
       mapLifted: LiftedList => LiftedCaseClass, mapPlain: PlainList => PlainCaseClass)(
       implicit columnShapes: Shape[FlatShapeLevel, LiftedList, PlainList, LiftedList], classTag: ClassTag[PlainCaseClass])
  extends MappedScalaProductShape[FlatShapeLevel, P, LiftedCaseClass, PlainCaseClass, LiftedCaseClass] {
  val shapes = columnShapes.asInstanceOf[HListShape[_,_,_,_]].shapes
  override def toMapped(v: Any) = {
    val folded = v.asInstanceOf[Product].productIterator.foldRight(HNil: HList)(_ :: _)
    mapPlain(folded.asInstanceOf[PlainList])
  }
  def buildValue(elems: IndexedSeq[Any]) = {
    val list = elems.foldRight(HNil: HList)(_ :: _)
    mapLifted(list.asInstanceOf[LiftedList])
  }
  def copy(s: Seq[Shape[_ <: ShapeLevel, _, _, _]]) = new HListCaseClassShape(mapLifted, mapPlain) { override val shapes = s }
}

Also, gist is available here: HListCaseClassShape.

Comments