r3stle55
r3stle55

Reputation: 480

LabelledGenerid.Aux implicit not available when using generic type in function body

I'm trying this and it works:

`
    case class Foo(name: String)

class Morphic(map: Map[String, Any]) { def add(k: String, v: Any) = { new Morphic((map + (k -> v))) } def to[T](): T = { def toClass[A]: ToCase[A] = new ToCase[A] // This is class to convert from Map to case class val res = toClass[Foo].from(map).get // <-- problem is here - cannot use T res.asInstanceOf[T] } } object testApp extends App { var m = new Morphic(Map[String, Any]()) var m1 = m.add("name", "john") println(m1.to[Foo]) }

I should use T instead of Foo in val res = toClass[T].from(map).get but it doesn't compile saying implicit is missing

toClass[T].from is a function creating a given type of case class from Map

How do I make that implicit (and possibly others on which .from relies) available?

I tried def to[T, H <: HList]()(implicit gen: LabelledGeneric.Aux[A, H]) = ... but then I need to specify both types when calling the .to and I can't figure out what to specify for H

Thanks

Upvotes: 0

Views: 90

Answers (1)

Dmytro Mitin
Dmytro Mitin

Reputation: 51693

You can transform a Map into HList, and then the HList into T:

import shapeless.{::, HList, HNil, LabelledGeneric, Witness}
import shapeless.labelled._

case class Foo(name: String)

trait MapToHList[L <: HList] {
  def apply(map: Map[String, Any]): Option[L]
}
object MapToHList {
  implicit object hNilMapToHList extends MapToHList[HNil] {
    override def apply(map: Map[String, Any]): Option[HNil] = Some(HNil)
  }

  implicit def hConsMapToHList[K <: Symbol, V, T <: HList](implicit
                                                           mapToHList: MapToHList[T],
                                                           witness: Witness.Aux[K]
                                                          ): MapToHList[FieldType[K, V] :: T] =
    new MapToHList[FieldType[K, V] :: T] {
      override def apply(map: Map[String, Any]): Option[FieldType[K, V] :: T] = {
        val str = witness.value.toString.tail
        for {
          v <- map.get(str)
          t <- mapToHList(map)
        } yield field[K](v.asInstanceOf[V]) :: t
      }
    }
}

trait ToCase[A] {
  def from(map: Map[String, Any]): Option[A]
}
object ToCase {
  implicit def mkToCase[A, L <: HList](implicit
                                       gen: LabelledGeneric.Aux[A, L],
                                       mapToHList: MapToHList[L]
                                      ): ToCase[A] =
    new ToCase[A] {
      override def from(map: Map[String, Any]): Option[A] = mapToHList(map).map(gen.from)
    }
}


class Morphic(map: Map[String, Any]) {

  def add(k: String, v: Any) = {
    new Morphic((map + (k -> v)))
  }

  def to[T](implicit toCase: ToCase[T]): T = toCase.from(map).get

}

object testApp extends App {
  var m = new Morphic(Map[String, Any]())
  var m1 = m.add("name", "john")
  println(m1.to[Foo]) // Foo(john)
}

I tried def to[T, H <: HList]()(implicit gen: LabelledGeneric.Aux[A, H]) ... but then I need to specify both types when calling the .to and I can't figure out what to specify for H

You can call it as m1.to[Foo, FieldType[Witness.`'name`.T, String] :: HNil]() or m1.to[Foo, Record.`'name -> String`.T]().

Upvotes: 1

Related Questions