0
votes

I am having problems converting between to case classes using LabelledGeneric

A simplified example of my code is below:

import shapeless._
import shapeless.record._

  def removeKeys[
    F <: Product,
    T <: Product,
    HF <: HList,
    HT <: Product
  ](
    from: F,
    removeField: String
  )(
    implicit genericFrom: LabelledGeneric.Aux[F, HF],
    genericTo: LabelledGeneric.Aux[T, HT]
  ): T = {
    val hListFrom = genericFrom.to(from) 
    val hListTo = hListFrom - Witness(removeField) // Missing implicit Remover 
    genericTo.from(hListTo) // If I remove multiple fields in a say foldLeft how do I ensure the resulting HList is of type HT?
  }

I am clearly missing a Remover - how should I conjure one up considering that I eventually want to foldLeft over a set of labels to remove hListFrom?

My ultimate intention is to select all the fields from case class F that exist on case class T with the same type.

For example, given:

case class A(a: Int, b: Double, c: Boolean)
case class B(b: Double, c: Boolean)

I want

def f[F <: Product, T <: Product](cc: F): T

so that f(A(1, 1.0, true)) returns B(1.0, true)

I guess I need the intersection of the two HLists however it has just occurred to me that I need to potentially reorder this depending on the order of the output case class constructor parameters.

I am enjoying learning Shapeless but there is quite a steep learning curve and many of the examples tend omit the details on how to ensure implicits are passed to generic code.

1
There are a couple of questions here (single removal vs. multiple removals with a fold). I think it'd be a better question if you focus on the first and ask the second as a follow-up. The intended usage is also unclear: it looks like callers will have to specify all type parameters explicitly, since HT in general won't uniquely identify a T. This presumably isn't what you want, and there are ways to work around it, but if you gave some example inputs, outputs, and usage it'd be a lot clearer.Travis Brown
Thanks for your reply. My ultimate intention is to select all the fields names on case class F that also exist on case class T with the same type and return an instance of case class T. I will edit the question to reflect this.Terry Dactyl
Thanks for updating! There's still the issue of how the output type should be determined, though. In general there might be many case classes with a subset of the members of A, in which case f(A(1, 1.0, true)) would be ambiguous.Travis Brown
The output type would be specified as a type parameter ignoring implicits the function would look like def convert[FromType, ToType](instance: FromType): ToTypeTerry Dactyl

1 Answers

1
votes

Regarding your first part of question,

  import shapeless._
  import shapeless.ops.record.Remover
  import shapeless.record._

  def removeKeys[
  F <: Product,
  T <: Product,
  HF <: HList,
  HT <: HList,
  V
  ](
     from: F,
     removeField: Witness
   )(
     implicit genericFrom: LabelledGeneric.Aux[F, HF],
     remover: Remover.Aux[HF, removeField.T, (V, HT)],
     genericTo: LabelledGeneric.Aux[T, HT],
  ): T = {
    val hListFrom = genericFrom.to(from)
    val hListTo = hListFrom - removeField
    genericTo.from(hListTo)
  }