0

Let's say I have a case class X(id: Int, name: String, age: Int) and some function (in my case, withUniqueGeneratedKeys in doobie) that returns X. If I have already defined X I am good.

But in my case the core data structure is something like:

case class XData(name: String, age: Int)
case class MaterializedX(id: Int, element: XData)

And of course I could write a line like case class X(id: Int, name: String, age: Int) to create X but it would be duplication of logic - whenever something about XData changes, I'd have to change the code for X as well. Intuitively, it feels like there should be a way to derive X from XData and MaterializedX. This transformation might mean some code, but it would save me lots of future work because I have many item types beyond X.

How could this be done? Happy to hear other approaches.

I am using Scala 3.1.2 in case this matters. Thank you!

Edit: Changed title per helpful comment, to make this question easier to understand.

1

2 Answers 2

2

I think you should be more clear about the question, I mean what title says is almost completely different from your question description (and what you might be actually looking for). Anyway, in my assumptions, what you need is probably a custom Read[MaterializedX] and Write[MaterializedX], as follows:

implicit val pointMaterializedX: Read[MaterializedX] =
  Read[(Int, String, Int)]
    .map { 
      case (id, name, age) => MaterializedX(id, XData(name, age)) 
    }

implicit val pointWrite: Write[MaterializedX] =
  Write[(Int, String, Int)]
    .contramap { materializedX => 
      (materializedX.id, materializedX.element.name, materializedX.element.age)
    }

More documentations here

1
  • Thanks! I didn't know that Doobie provides tools to make this easy, but I totally agree that the terminology is confusing, I edited above (and added a note that this is added so your answer still makes sense). I tried the solution and it works well!
    – cgold
    Commented Aug 23, 2022 at 1:18
0

This works out of the box with doobie. Nested case classes get flattened into one row. The following code compiles without defining any custom decoders.

case class XData(name: String, age: Int)
case class MaterializedX(id: Int, element: XData)

implicitly[doobie.Read[MaterializedX]]
implicitly[doobie.Write[MaterializedX]]

Not the answer you're looking for? Browse other questions tagged or ask your own question.