我昨天和几位同事一起探索了Shapeless,我们决定编写一个玩具方法,在案例类的第一个参数中添加一个,当该参数为Int
时:
def addOneToCaseClass[C, H <: HList, E, T <: HList]
(c: C)
(implicit gen: Generic.Aux[C, H],
h: IsHCons.Aux[H, E, T],
ev: E =:= Int,
ev2: (Int :: T) =:= H
): C = {
val hList = gen.to(c)
val elem = hList.head
val tail = hList.tail
val newElem = elem + 1
gen.from(newElem :: tail)
}
在我看来,ev2
参数是多余的 - 当然可以推断E :: T =:= Int :: T
,但编译器无法实现这一点。
有什么特别的原因吗?
答案 0 :(得分:2)
你的直觉是合理的,但不幸的是,Scala编译器不够聪明,无法从ev2
和h
派生ev
。问题是h
仅确定H
分解为E :: T
,它没有建立相反的结果,即E
和T
合并为{ {1}}。
我能提出的最简洁的表述与你原来的相似,但只有一个证人,
H
我们可以使用def addOneToCaseClass[C, R <: HList, T <: HList](c: C)
(implicit
gen: Generic.Aux[C, R],
h: IsHCons.Aux[R, Int, T],
ev: (Int :: T) =:= R) = {
val hList = gen.to(c)
val elem = hList.head
val tail = hList.tail
gen.from(elem+1 :: tail)
}
消除E =:= Int
证明h
分解为R
的证据。不过,我们仍然需要证明Int :: T
等于Int :: T
,以便使用更新后的元素返回R
。