Shapeless - превратить класс дела в другой с полями в другом порядке
Я имею в виду сделать что-то похожее на Безопасное копирование полей между классами 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
, который будет отлично работать для записей (в конце концов, это просто специальные списки).
Если вам нужен хороший синтаксис, вам нужно использовать трюк, подобный следующему, для отделения списка параметров типа с целью (которую вы хотите явно указать) от списка параметров типа со всеми другими вещами (которые вы хотите вывести):
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)
PS Заметил, что есть пример на GitHub.