The following compiles:
object Run1 extends App {
import shapeless._
import syntax.std.traversable._
case class Container[T](x: T)
Seq(Container(1), Container("x")).toHList[Container[Int] :: Container[String] :: HNil]
}
But this does not:
object Run2 extends App {
import shapeless._
import syntax.std.traversable._
class Container[T](val x: T)
Seq(new Container(1), new Container("x")).toHList[Container[Int] :: Container[String] :: HNil]
}
It fails with the following errors:
Error:(40, 52) could not find implicit value for parameter fl: shapeless.ops.traversable.FromTraversable[shapeless.:: [com.adaje.service.table.Run2.Container[Int],shapeless.::[com.adaje.service.table.Run2.Container[String],shapeless.HNil]]]
Seq(new Container(1), new Container("x")).toHList[Container[Int] :: Container[String] :: HNil]
^
Why does the second program not work and is there anything that can be added so that it does?
Thanks
The FromTraversable type class requires Typeable instances for the element types. Shapeless provides these off the shelf for case classes, but not for arbitrarily defined classes. You can define your own pretty easily, though:
import shapeless._, shapeless.syntax.std.traversable._
class Container[T](val x: T)
implicit def containerTypeable[A: Typeable]: Typeable[Container[A]] =
new Typeable[Container[A]] {
def cast(t: Any): Option[Container[A]] = t match {
case c: Container[_] => Typeable[A].cast(c.x).map(new Container(_))
case _ => None
}
def describe: String = s"Container[${ Typeable[A].describe }]"
}
And then:
scala> val cs = Seq(new Container(1), new Container("x"))
cs: Seq[Container[_ >: String with Int]] = List(Container@3c3c89b2, Container@1273a053)
scala> cs.toHList[Container[Int] :: Container[String] :: HNil]
res0: Option[shapeless.::[Container[Int],shapeless.::[Container[String],shapeless.HNil]]] = Some(Container@357c808b :: Container@607bcaf5 :: HNil)
And also:
scala> cs.reverse.toHList[Container[Int] :: Container[String] :: HNil]
res1: Option[shapeless.::[Container[Int],shapeless.::[Container[String],shapeless.HNil]]] = None
scalac's -Xlog-implicits option can be handy in cases like this—it'll make it clear that what was missing in your original non-case-class attempt was the Typeable instances.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With