如何映射一个 Shapeless 的 HList[Nat]?

4

我有一个自然数类型的HList,我想对它进行映射操作

object NatToString extends Poly1 {
    implicit def caseNat = at[Nat](_.toString)
}

val list = _5 :: _3 :: HNil
list.map(NatToString) 

这段代码无法编译并抛出以下错误:
无法找到隐式参数mapper,其类型为shapeless.ops.hlist.Mapper[Main.Nat_to_String.type,shapeless.::[shapeless.Nat._5,shapeless.::[shapeless.Nat._3,shapeless.HNil]]]。
但是如果我用Int(或String、List等)代替Nat,它就可以完美运行。
我该如何在HList上进行Nat映射?
1个回答

5
问题在于Poly1.Case的类型参数不是协变的。请看下面的例子:
trait Foo
trait Bar extends Foo

val foo = new Foo {}
var bar = new Bar {}

object fooIdentity extends Poly1 {
  implicit def caseFoo = at[Foo](identity)
}

现在fooIdentity(foo)可以编译,但fooIdentity(bar)不能。
在你的情况下,HList的成员被静态类型化为_5_3。这些都是Nat的子类型,但NatToString并不关心,因为它的唯一情况是寻找一个被静态类型化为Nat的东西。
关键是只需要在case中添加一个类型参数:
object NatToString extends Poly1 {
  implicit def caseNat[N <: Nat] = at[N](_.toString)
}

通常情况下,直接使用 Nat 并不是你想要的,你几乎总是需要一个特定的子类型。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接