Shapeless-превратите класс case в другой с полями в другом порядке

Я думаю сделать что-то похожее на безопасное копирование полей между классами case различных типов но с переупорядочить поля, т. е.

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

и я хотел бы иметь что-то, чтобы превратить A(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)))
                                                           ^

как изменить порядок полей в записи (?) таким образом, это может работать с минимальным шаблоном?

2 ответов


Я должен оставить это на мили, но это счастливый час, откуда я, и я не могу сопротивляться. Как он указывает в комментарии выше, ключ ops.hlist.Align, который будет работать просто отлично для записей (которые являются просто специальными hlists, в конце концов).

если вы хотите хороший синтаксис, вам нужно использовать трюк, как показано ниже, для разделения списка параметров типа с целью (которую вы хотите предоставить явно) из списка параметров типа со всеми другими вещами (которые вы хотите быть вывод):

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)

обратите внимание, что поиск экземпляров выравнивания будет стоить дорого во время компиляции для больших классов case.


как заметил @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)

П. С. заметил, что есть пример на GitHub.