当您编写 IsHCons.Aux[L, TypedMap, L] 时,您要求提供证据证明 hlist L 具有头部 TypedMap 和尾部 L,这意味着它是一个无限的 hlist,这是不可能的,因为 Scala 没有' 不允许这种任意递归类型(例如,尝试编写 type Foo = Int :: Foo 之类的东西——你会得到一个“非法循环引用”错误)。这也可能不是你想要的。
一般来说,你不太可能在 Shapeless 中使用 IsHCons,因为在类型中指明你想要的结构几乎总是更好。例如,以下两个定义做同样的事情:
import shapeless._, ops.hlist.IsHCons
def foo[L <: HList](l: L)(implicit ev: IsHCons[L]) = ev.head(l)
还有:
def foo[H, T <: HList](l: H :: T) = l.head
但是第二个显然更可取(它更清楚,它不需要在编译时找到额外的类型类实例等),并且几乎总是可以编写任何你想要这样做的东西.
还请注意,需要 IsHCons 实例意味着此处的递归将无法按所述工作 - 您不能在 HNil 上调用 get,因为编译器无法证明它是 @987654332 @(因为不是)。
你确定你需要一个 hlist 吗?如果您要求 hlist 的所有成员都是 TypedMap 类型,您不妨使用 Shapeless 的 Sized(如果您希望该类型捕获长度),甚至只是一个普通的旧 List。
如果您真的非常想在这里使用HList,我建议您编写一个新的类型类:
trait FindField[L <: HList] {
def find(key: IntegerField, l: L): Option[Int]
}
object FindField {
implicit val findFieldHNil: FindField[HNil] = new FindField[HNil] {
def find(key: IntegerField, l: HNil) = None
}
implicit def findFieldHCons[H <: TypedMap, T <: HList](implicit
fft: FindField[T]
): FindField[H :: T] = new FindField[H :: T] {
def find(key: IntegerField, l: H :: T) = if (l.head.head == key)
Some(l.head.tail.head)
else fft.find(key, l.tail)
}
}
def get[L <: HList](key: IntegerField, l: L)(implicit
ffl: FindField[L]
): Option[Int] = ffl.find(key, l)
然后:
scala> get(IntegerField("year"), test)
res3: Option[Int] = Some(23)
scala> get(IntegerField("foo"), test)
res4: Option[Int] = None
这是 Shapeless 中非常常见的模式——你归纳地描述如何对一个空的 hlist 执行操作,然后在一个 hlist 上执行一个头部附加到一个你知道如何执行操作的尾部的操作,等等。