scalagenericsshapelesshlist

How to convert between to case classes with `mostly the same` fields using Scala Shapeless


Here I have to case classes which have mostly the same fields.

  final case class Id(id: String) // Param Class
  final case class Age(id: Id, age: Int) // Param Class

  final case class A(id: Id, data: Map[String, Any], age: Age) extends Presentable[A, APre] // Main Class 1
  final case class APre(id: String, data: Map[String, Any], age: Int) // Main Class 2

Here A and APre are my main classes.

Now I want to convert between this two class using Shapeless, so I write the following pseudo function:

trait Presentable[E, P] {
  def makePresentation[ET <: HList, PT <: HList](entity: E)(func : ET => PT)(implicit entGen: LabelledGeneric.Aux[E, ET], preGen: LabelledGeneric.Aux[P, PT]): P = {
    val entList = entGen.to(entity)
    preGen.from(func(entList))
  }
}

Here func is a mapper mapping the HList of A to HList of APre (or vice versa).

And I want to use the function like this:

  val age = Age(Id("age_1"), 18)
  val a = A(Id("id"), Map("tag1" -> "value1", "tag2" -> "value2"), age)

  val pre = a.makePresentation { entList =>
    entList.updateWith('id)((id: Id) => id.id).updateWith('age)((a: Age) => a.age)
  }

Here I can imply the mapping function myself. So I can convert any two case classes

So questions are: 1. How can I convert this two classes using shapeless? 2. In fact, I have tons of pairs of class like A to APre. So I want a trait to extract this convert function using generic. How to write this function?


Solution

  • Disclaimer: I'm one of chimney's authors.

    In earlier releases of chimney we implemented exactly what you are asking about - conversion between mostly identical case classes using shapeless.

    I wouldn't recommend writing it by hand as there are some corner cases to consider (creating values if new object is missing some, transforming the fields that changed type/name, Java Beans, value classes, etc) and then you have to come up with how would you configure it, so if you need to have shapeless-bases solution look at the code from 0.1.10.

    However, since 0.2.0, we rewritten the implementation into macros since if you had bigger case classes to transform (e.g. 12 fields or more) some derivations could compute several minutes(!) with no hope of improvement unless we dropped some of cases we support.

    If you're just looking for a way of handling your transformations, then use newest chimney and call it a day.