编译如下:
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]
}
但这不是:
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]
}
失败并显示以下错误:
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]
^
为什么第二个程序不起作用,并且有什么可以添加的呢?
谢谢
最佳答案
FromTraversable
类型类需要元素类型的Typeable
实例。 Shapeless为案例类提供了现成的功能,但不适用于任意定义的类。不过,您可以很轻松地定义自己的:
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 }]"
}
然后:
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)
并且:
scala> cs.reverse.toHList[Container[Int] :: Container[String] :: HNil]
res1: Option[shapeless.::[Container[Int],shapeless.::[Container[String],shapeless.HNil]]] = None
在这种情况下,scalac的
-Xlog-implicits
选项可能很方便-可以清楚地表明,原始的非案例类尝试中缺少的是Typeable
实例。关于scala - 无形.toHList的行为,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/37015011/