类似于这里的这个问题( 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]]]
我是否遗漏了一些重要的东西?
最佳答案
您的示例有一个非常小的问题。你有,
val options =
('awesomeString ->> "a") ::
('epicInt ->> 5:Int) ::
HNil
如果您将其粘贴到 REPL 中,您会发现您已经忘记了 'epicInt
键入记录的最后一个元素。这是因为类型归属的绑定(bind)不如 ->>
紧密。运算符,因此实际上您首先用它的键标记了您的值,然后立即再次将其丢弃。这将为您留下有效的 HList
,但不幸的是,没有一个适合成为唱片的形状。解决方法是完全删除类型归属,或使用括号(即 (5: Int)
。
更大的问题是您使用FieldPoly
这里。 FieldPoly
适用于您要操作的 key 静态已知的情况。这里的情况并非如此:类型变量 K
是免费的并且必须被推断。不幸的是,这是不可能的:它必须从 atField
的第一个参数推断出来。但这又取决于K
通过 witness
的隐式定义.
可能是 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 _))
}
关于scala - 使用 Shapeless Polyfil 在记录上进行一般映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26751711/