If you want to do this with Shapeless, I would strongly suggest defining a custom type class that handles the complex part and allows you to keep this material separate from the rest of your logic.
In this case, it seems that the hard part of what you are specifically trying to do is get a mapping from field names to string lengths for all members of the String class of the case class. Here is a type class that does this:
import shapeless._, shapeless.labelled.FieldType trait StringFieldLengths[A] { def apply(a: A): Map[String, Int] } object StringFieldLengths extends LowPriorityStringFieldLengths { implicit val hnilInstance: StringFieldLengths[HNil] = new StringFieldLengths[HNil] { def apply(a: HNil): Map[String, Int] = Map.empty } implicit def caseClassInstance[A, R <: HList](implicit gen: LabelledGeneric.Aux[A, R], sfl: StringFieldLengths[R] ): StringFieldLengths[A] = new StringFieldLengths[A] { def apply(a: A): Map[String, Int] = sfl(gen.to(a)) } implicit def hconsStringInstance[K <: Symbol, T <: HList](implicit sfl: StringFieldLengths[T], key: Witness.Aux[K] ): StringFieldLengths[FieldType[K, String] :: T] = new StringFieldLengths[FieldType[K, String] :: T] { def apply(a: FieldType[K, String] :: T): Map[String, Int] = sfl(a.tail).updated(key.value.name, a.head.length) } } sealed class LowPriorityStringFieldLengths { implicit def hconsInstance[K, V, T <: HList](implicit sfl: StringFieldLengths[T] ): StringFieldLengths[FieldType[K, V] :: T] = new StringFieldLengths[FieldType[K, V] :: T] { def apply(a: FieldType[K, V] :: T): Map[String, Int] = sfl(a.tail) } }
It looks complicated, but as soon as you start working with Shapeless, you will learn to write such things in a dream.
Now you can write the logic of your operation in a relatively simple way:
def maxStringLengths[A: StringFieldLengths](as: List[A]): Map[String, Int] = as.map(implicitly[StringFieldLengths[A]].apply).foldLeft( Map.empty[String, Int] ) { case (x, y) => x.foldLeft(y) { case (acc, (k, v)) => acc.updated(k, acc.get(k).fold(v)(accV => math.max(accV, v))) } }
And then (given rows , as defined in the question):
scala> maxStringLengths(rows).foreach(println) (bankCharges,3) (overTime,3) (mgmtFee,5) (email,9) (copyOfVisa,3)
This will work for absolutely any class of case.
If this is a one-time thing, you can use reflection at runtime, or you can use the Poly1 approach in Giovanni Caporaretti's answer - it is less general and mixes the different parts of the solution this way I don't prefer, but it should work fine. If this is what you do a lot, I would suggest the approach that I have given here.