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.

Другие вопросы по тегам