类似于这个问题(Mapping over Shapeless record),我试图映射一个简单的无形记录(在这种情况下,如果我遇到Int
类型的值,我想将其转换为一个Double
)。
object Main extends App {
import shapeless._ ; import syntax.singleton._ ; import record._
import ops.record._
import syntax.singleton._
case class S(int:Int,t:String)
val s = S(3,"a")
val gen = LabelledGeneric[S]
val rec = gen.to(s)
val extended = rec + ('inPrint ->> true)
val removed = rec - 'int
val keys = Keys[gen.Repr]
val options =
('awesomeString ->> "a") ::
('epicInt ->> 5:Int) ::
HNil
def intToDouble(i:Int):Double = i.toDouble
object bind extends FieldPoly {
implicit def rpb[T, K](implicit witness: Witness.Aux[K]): Case.Aux[
FieldType[K, Int],
FieldType[K, Double]
] = atField(witness)(intToDouble)
}
val z = options.map(bind)
}
但是当我尝试编译时,我收到以下错误
could not find implicit value for parameter mapper: shapeless.ops.hlist.Mapper[Main.bind.type,shapeless.::[String with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("awesomeString")],String],shapeless.::[Int,shapeless.HNil]]]
是否有一些我不知道的重要事项?
答案 0 :(得分:2)
你的例子有一个非常小的问题。你有,
val options =
('awesomeString ->> "a") ::
('epicInt ->> 5:Int) ::
HNil
如果将其粘贴到REPL中,您将看到您在记录的最后一个元素中丢失了'epicInt
键的跟踪。这是因为类型归属比->>
运算符绑定得更紧,因此您实际上首先使用其键标记了您的值,然后立即再次将其抛出。这将为您留下有效的HList
,但不幸的是,它不是一个正确的形状作为记录。修复方法是完全删除类型归属,或使用括号(即(5: Int)
。
更大的问题是你在这里使用FieldPoly
。 FieldPoly
适用于您想要操作的密钥是静态已知的情况。情况并非如此:类型变量K
是免费的,必须推断出来。不幸的是,它不可能是:它必须从第一个参数推断到atField
,但反过来,依赖于K
通过隐含的witness
定义。 / p>
可能会更好地匹配您的方案的FieldPoly
的不同变体将是有用的。与此同时,在整个领域上运作的普通Poly1
(即与价值相结合的关键字)将做你想要的,
trait bind0 extends Poly1 {
implicit def default[E] = at[E](identity) // default case for non-Int fields
}
object bind extends bind0 {
implicit def caseInt[K] = // case for fields with Int values
at[FieldType[K, Int]](field[K](intToDouble _))
}