使用Shapeless的方法需要的证据多于必要的证据

Noe*_*l M 5 scala shapeless

我昨天和一些同事一起探索了Shapeless,我们决定编写一个玩具方法,将一个添加到case类的第一个参数,当该参数为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)
}
Run Code Online (Sandbox Code Playgroud)

在我看来,ev2参数是多余的 - 当然可以推断出E :: T =:= Int :: T,但编译器无法实现这一点.

有什么特别的原因吗?

Mil*_*bin 2

您的直觉是合理的,但不幸的是 Scala 编译器不够聪明,无法ev2h和派生ev。问题是h只建立了H分解为E :: T,而没有建立逆过程,即ET结合为等于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)
}
Run Code Online (Sandbox Code Playgroud)

在这里,我们可以消除证明,E =:= Int通过使用h来证明R分解为Int :: T。然而,我们仍然需要证明这Int :: T等于R向后移动gen更新的元素。