24

我正在考虑做一些类似于在不同类型的案例类之间安全复制字段但使用重新排序的字段的事情,即

case class A(foo: Int, bar: Int)
case class B(bar: Int, foo: Int)

然而,我想有一些东西可以把 aA(3, 4)变成B(4, 3)- 无形的'LabelledGeneric

LabelledGeneric[B].from(LabelledGeneric[A].to(A(12, 13)))

结果是

<console>:15: error: type mismatch;
 found   : shapeless.::[shapeless.record.FieldType[shapeless.tag.@@[Symbol,String("foo")],Int],shapeless.::[shapeless.record.FieldType[shapeless.tag.@@[Symbol,String("bar")],Int],shapeless.HNil]]
    (which expands to)  shapeless.::[Int with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("foo")],Int],shapeless.::[Int with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("bar")],Int],shapeless.HNil]]
 required: shapeless.::[shapeless.record.FieldType[shapeless.tag.@@[Symbol,String("bar")],Int],shapeless.::[shapeless.record.FieldType[shapeless.tag.@@[Symbol,String("foo")],Int],shapeless.HNil]]
    (which expands to)  shapeless.::[Int with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("bar")],Int],shapeless.::[Int with shapeless.record.KeyTag[Symbol with shapeless.tag.Tagged[String("foo")],Int],shapeless.HNil]]
              LabelledGeneric[B].from(LabelledGeneric[A].to(A(12, 13)))
                                                           ^

如何对记录中的字段(?)重新排序,以便使用最少的样板文件?

4

2 回答 2

37

我应该把这个留给迈尔斯,但这是我来自的欢乐时光,我无法抗拒。正如他在上面的评论中指出的那样,关键是ops.hlist.Align,它适用于记录(毕竟只是特殊的 hlist)。

如果你想要一个好的语法,你需要使用类似下面的技巧将类型参数列表与目标(你想显式提供)与类型参数列表与所有其他东西(你想被推断)分开):

import shapeless._, ops.hlist.Align

class SameFieldsConverter[T] {
  def apply[S, SR <: HList, TR <: HList](s: S)(implicit
    genS: LabelledGeneric.Aux[S, SR],
    genT: LabelledGeneric.Aux[T, TR],
    align: Align[SR, TR]
  ) = genT.from(align(genS.to(s)))
}

def convertTo[T] = new SameFieldsConverter[T]

接着:

case class A(foo: Int, bar: Int)
case class B(bar: Int, foo: Int)

接着:

scala> convertTo[B](A(12, 13))
res0: B = B(13,12)

请注意,对于大型案例类,在编译时查找对齐实例会变得很昂贵。

于 2015-03-24T22:22:11.673 回答
17

正如@MilesSabin(神一样的无形创造者)所注意到的,有一个对齐操作,它的用法如下:

import ops.hlist.Align

val aGen = LabelledGeneric[A]
val bGen = LabelledGeneric[B]
val align = Align[aGen.Repr, bGen.Repr]
bGen.from(align(aGen.to(A(12, 13)))) //> res0: B = B(13,12)

PS 注意到GitHub 上有一个示例

于 2015-03-24T22:20:15.903 回答