使用Shapeless Polyfil一般映射记录

时间:2014-11-05 07:23:25

标签: scala generics shapeless

类似于这个问题(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]]]

是否有一些我不知道的重要事项?

1 个答案:

答案 0 :(得分:2)

你的例子有一个非常小的问题。你有,

val options = 
  ('awesomeString ->> "a") ::
  ('epicInt ->> 5:Int) ::
  HNil

如果将其粘贴到REPL中,您将看到您在记录的最后一个元素中丢失了'epicInt键的跟踪。这是因为类型归属比->>运算符绑定得更紧,因此您实际上首先使用其键标记了您的值,然后立即再次将其抛出。这将为您留下有效的HList,但不幸的是,它不是一个正确的形状作为记录。修复方法是完全删除类型归属,或使用括号(即(5: Int)

更大的问题是你在这里使用FieldPolyFieldPoly适用于您想要操作的密钥是静态已知的情况。情况并非如此:类型变量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 _))
}