I am trying to convert Map[String, AttributeValue]
to Scala objects. AttributeValue
represents data in DynamoDB. FromAttributeValue
in the below stub is used to convert basic cases
Following is code stub:
trait FromMap[L] {
def apply(m: Map[String, AttributeValue]): Option[L]
}
object FromMap extends LowerPriorityFromMapInstances {
implicit val hnilFromMap: FromMap[HNil] = ???
implicit def hconsFromMap0[K <: Symbol, V, T <: HList](implicit
witness: => Witness.Aux[K],
fromAttributeValue: => Lazy[FromAttributeValue[V]],
fromMapT: => FromMap[T],
): FromMap[FieldType[K, V] :: T] = ???
implicit def cnilFromMap: FromMap[CNil] = ???
implicit def coproductFromMap[K <: Symbol, H, T <: Coproduct](implicit
wit: => Witness.Aux[K],
fromMapH: => Lazy[FromMap[H]],
fromMapT: => FromMap[T]
): FromMap[FieldType[K, H] :+: T] = ???
}
trait LowerPriorityFromMapInstances {
//Other instances as required
implicit def hConsFromMap1[K <: Symbol, V, R <: HList, T <: HList](implicit
wit: => Witness.Aux[K],
gen: => LabelledGeneric.Aux[V, R],
fromMapH: => Lazy[FromMap[R]],
fromMapT: => FromMap[T]
): FromMap[FieldType[K, V] :: T] = ???
}
Following is a scastie for the same.
Compiler is able to find derivations for all cases except for sealed traits. How can I make this work?
Firstly, since your representation types are now not only HList
s, but also Coproduct
s, you should remove upper bounds <: HList
in these two places
implicit class FromMapOps[L /*<: HList*/](val a: Map[String, AttributeValue]) extends AnyVal { ...
// ^^^^^^^^
implicit def hConsFromMap1[K <: Symbol, V, R /*<: HList*/, T <: HList](implicit ...
// ^^^^^^^^
Secondly, in implicit class FromMapOps
you're mixing recursion logic with definition of extension method, so you don't actually define an instance of type class FromMap
for a case class (and sealed trait). But if you start to resolve Map.empty[String, AttributeValue].toObj[ContainerUser]
then you'll see that you need such instance, namely FromMap
for GroupContainer
Map.empty[String, AttributeValue].toObj[ContainerUser]
implicitly[FromMap[FieldType[Witness.`'GroupContainer`.T, GroupContainer] :+: CNil]](
FromMap.coproductFromMap(
implicitly[Witness.Aux[Witness.`'GroupContainer`.T]],
Lazy(implicitly[FromMap[GroupContainer]]),
implicitly[FromMap[CNil]]
)
)
implicitly[FromMap[GroupContainer]]
So better don't mix recursion logic with logic defining extension method, keep logic defining instances of a type class and logic defining extension method separate. Try to define an instance of type class
implicit def genericFromMap[A, L](implicit
gen: => LabelledGeneric.Aux[A, L],
fromMap: => FromMap[L],
): FromMap[A] = ???
and an extension method
implicit class FromMapOps[L](val a: Map[String, AttributeValue]) extends AnyVal {
def toObj[A](implicit
fromMap: FromMap[A]
): Option[A] = fromMap(a)
}
https://scastie.scala-lang.org/DmytroMitin/JwhJuXiXRBOT08RjZyywKg/2 (NotImplementedError
means that the code compiles.)
You can find several SO questions about converting between a case class and map (search for case class to map scala, map to case class scala).