Shapeless Generic[] going deeper

Hello!

Story:
The slick lib has a strange method to transform back and forth between columns and case-classes (actually its not so strange if you get used to it). I wanted to create a new trait, which can add createdBy createdAt, updatedBy updatedAt fields to a random table without a lot of refactor/code rewrite.

Problem:
I need to create a func like this:
def * = (a, b, c) <> (A.tuppled, A.unapply) where a is sth like a Rep[String]
(I can use def * = (a :: b :: c :: HNil).mappedWith(Generic[A]) too with slickless.)

My idea:
I have 2 tuples (one for the audit fields, and one for the concrete DBO).

val tuple1 = (1, 3, "asd")
val tuple2 = ("dsa", 5L, 2.3, 5.7)

I can transform them to satisfy the left side of the <>

val listOfTuples = tuple1 :: tuple2 :: HNil
//or
val hlist1 = tuple1.productElements
val hlist2 = tuple2.productElements
val listOfLists = hlist1 :: hlist2 :: HNil

I can create the case-classes too (the type of B will be the actual DBO):

case class Foo(a: Int, b: Int, c: String)
case class Bar(d: String, e: Long, f: Double, g: Double)

case class Both[B](f: Foo, b: B)

And now, I need a Generic[Both[Bar]] which is deeper than the default generated code.

val g = Generic[Both[Bar]] //Foo :: Bar :: HNil
val needed = DeeperGeneric[Both[Bar]] //(Int :: Int :: String :: HNil) :: (String :: Long :: Double :: Double :: HNil) :: HNil

So basically I want a DeeperGeneric implementation if it is possible :smiley: (I need to transform from-to.)

The closest I get to this problem is sth like:

val generic = Generic[Bar]
def * = (auditTuple :: fieldTuple :: HNil).mappedWith(new LessGeneric(generic.to, generic.from))

Which is not as bad, but still I think there is a better way…

Full scratch (I want to factor out the T from this code):

import shapeless.Generic.Aux
import shapeless._
import syntax.std.tuple._

import scala.reflect.ClassTag

val tuple1 = (1, 3, "asd")
val tuple2 = ("dsa", 5L, 2.3, 5.7)

val listOfTuples = tuple1 :: tuple2 :: HNil

val hlist1 = tuple1.productElements
val hlist2 = tuple2.productElements

val listOfLists = hlist1 :: hlist2 :: HNil

case class Foo(a: Int, b: Int, c: String)
case class Bar(d: String, e: Long, f: Double, g: Double)

case class Both[B](f: Foo, b: B)

class LessGeneric[B, T](to: B => T, from: T => B) extends Generic[Both[B]] {
  type Repr = genf.Repr :: T :: shapeless.HNil

  val genf = Generic[Foo]

  /** Convert an instance of the concrete type to the generic value representation */
  def to(t : Both[B]) : Repr = {
    genf.to(t.f) :: to(t.b) :: HNil
  }

  /** Convert an instance of the generic representation to an instance of the concrete type */
  def from(r : Repr) : Both[B] = {
    Both(
      genf.from(r.head),
      from(r.tail.head)
    )
  }
}

val g3 = Generic[Bar]
val lessGen = new LessGeneric(g3.to, g3.from)
lessGen.from(listOfLists)

Any idea, maybe a concrete proof that it can’t be done would be appreciated. (I dropped the idea of using this bcs of other business reasons, but I still want to know the answer if there is any :smiley: )

EDIT: Tried to force the formatter to use scala code-blocks.

If you give the case classes the same nesting pattern as the tuples, you can compose MappedProjections on the left side of <>.