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): ToType - Terry 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)
  }