2

Is it possible to get scala case class field's names and types with shapeless?

I've tried like this (T is case class):

trait Cpo[T] {

def withPrimaryKey[R <: HList, K, V <: HList](f: Seq[Symbol] => Seq[Symbol])(
    implicit labellGeneric: LabelledGeneric.Aux[T, R], keys: Keys.Aux[R, K],
    ktl: hlist.ToList[K, Symbol]): Cpo[T]
}

but I only can get field's name.

Zlaja

zlaja
  • 1,351
  • 3
  • 13
  • 23

3 Answers3

4

Try

object typeablePoly extends Poly1 {
  implicit def default[A](implicit typeable: Typeable[A]): Case.Aux[A, String] = at(_ => typeable.describe)
}

trait Cpo[T] {

  def withPrimaryKey[R <: HList, K <: HList, V <: HList, V1 <: HList](f: Seq[Symbol] => Seq[Symbol])(implicit
    labellGeneric: LabelledGeneric.Aux[T, R],
    keys: Keys.Aux[R, K],
    ktl: hlist.ToList[K, Symbol],
    values: Values.Aux[R, V],
    mapper: hlist.Mapper.Aux[typeablePoly.type, V, V1],
    vtl: hlist.ToList[V1, String]
  ): Cpo[T] 
}

Now ktl gives list of field names (as Symbols) and vtl gives list of field types (as Strings).


Try

  object typeablePoly extends Poly1 {
    implicit def default[A](implicit typeable: Typeable[A]): Case.Aux[A, String] = at(_ => typeable.describe)
  }

  object nullPoly extends Poly0 {
    implicit def default[A]: ProductCase.Aux[HNil, A] = at(null.asInstanceOf[A])
  }

  trait Cpo[T] {

    def withPrimaryKey[R <: HList, K <: HList, V <: HList, V1 <: HList](f: Seq[Symbol] => Seq[Symbol])(implicit
      labellGeneric: LabelledGeneric.Aux[T, R],
      keys: Keys.Aux[R, K],
      ktl: hlist.ToList[K, Symbol],
      values: Values.Aux[R, V],
      mapper: hlist.Mapper.Aux[typeablePoly.type, V, V1],
      fillWith: hlist.FillWith[nullPoly.type, V],
      vtl: hlist.ToList[V1, String]
    ): Cpo[T] = {
      println(ktl(keys())) // List('i, 's)
      println(vtl(mapper(fillWith()))) // List(Int, String)
      ???
    }
  }

  case class MyClass(i: Int, s: String)
  new Cpo[MyClass] {}.withPrimaryKey(identity)
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • 2
    If I figured out correctly, to get fields types I need to have instance of T? Is it possible to get fields types just using type T? – zlaja Apr 04 '19 at 09:19
0

You definitely can get field names. For example, here you can find how to write your shapeless-based generic derivation mechanism: Bits of shapeless part 2. More specific, you should look at Deriving case Classes part, there is a function which derives encoder for arbitrary case class, its signature is:

implicit def hconsToJson[Key <: Symbol, Head, Tail <: HList](
    implicit key: Witness.Aux[Key],
    headWrites: JsonWrites[Head],
    tailWrites: JsonWrites[Tail])
    : JsonWrites[FieldType[Key, Head] :: Tail] = ???

Hence, key parameter allows you to acces the field name of the certain field. For the types, the only known way to me is to use reflection. Read this for details Scala manual on type tags.

Iva Kam
  • 932
  • 4
  • 13
0

If using shapeless is not necessary, you can get the type as well as value using Product class in scala

case class Test(x:Int,y:String,z:Boolean)
println(getGeyNameValueType(Test(1,"a",true)).foreach(println))

def getGeyNameValueType(inp: Product): Iterator[(Any, Class[_])] = {
    val itr = inp.productIterator
    for {
      item <- itr
    } yield (item, item.getClass)
}

The output is

(1,class java.lang.Integer)
(a,class java.lang.String)
(true,class java.lang.Boolean)
()
  • Yes, but it must have instance of T. I want to do that without instance. I made it with shapeless. – zlaja Apr 07 '19 at 08:49