Подтвердить что ты не робот

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[[email protected]@[Symbol,String("foo")],Int],shapeless.::[shapeless.record.FieldType[[email protected]@[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[[email protected]@[Symbol,String("bar")],Int],shapeless.::[shapeless.record.FieldType[[email protected]@[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)))
                                                           ^

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

4b9b3361

Ответ 1

Я должен оставить это для Майлза, но это счастливый час, откуда я, и я не могу сопротивляться. Как он указывает в комментарии выше, ключ 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)

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

Ответ 2

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

P.S. Заметил, что есть пример на GitHub.