使用 Shapeless 的方法需要比必要更多的证据
More evidence needed than necessary for method using Shapeless
我昨天和几位同事一起探索 Shapeless,我们决定编写一个玩具方法来将一个添加到案例 class 的第一个参数,当该参数是 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
,但编译器无法做到这一点。
有什么特别的原因吗?
您的直觉是合理的,但不幸的是 Scala 编译器不够聪明,无法从 h
和 ev
派生出 ev2
。问题是 h
只确定 H
分解为 E :: T
,它没有确定相反的情况,即 E
和 T
合并等于 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)
}
这里我们可以通过使用 h
证明 R
分解为 Int :: T
来消除 E =:= Int
的证明。然而,我们仍然需要证明 Int :: T
等于 R
以使用更新后的元素返回 gen
。
我昨天和几位同事一起探索 Shapeless,我们决定编写一个玩具方法来将一个添加到案例 class 的第一个参数,当该参数是 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
,但编译器无法做到这一点。
有什么特别的原因吗?
您的直觉是合理的,但不幸的是 Scala 编译器不够聪明,无法从 h
和 ev
派生出 ev2
。问题是 h
只确定 H
分解为 E :: T
,它没有确定相反的情况,即 E
和 T
合并等于 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)
}
这里我们可以通过使用 h
证明 R
分解为 Int :: T
来消除 E =:= Int
的证明。然而,我们仍然需要证明 Int :: T
等于 R
以使用更新后的元素返回 gen
。