@MariaLiviaCh
The Path to Generic Endpoints using Shapeless | @marry16_08
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eleifend condimentum metus, at pretium orci sagittis et. Cras euismod justo ut tellus venenatis, et fermentum lectus feugiat. Nam pellentesque massa ac nulla semper ultricies. In eleifend tempor cursus.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec eleifend condimentum metus, at pretium orci sagittis et.
Cras euismod justo ut tellus venenatis, et fermentum lectus feugiat. Nam pellentesque massa ac nulla semper ultricies. In eleifend tempor cursus.
The Path to Generic Endpoints using Shapeless | @marry16_08
The Path to Generic Endpoints using Shapeless | @marry16_08
The Path to Generic Endpoints using Shapeless | @marry16_08
PATCH /api/:atomType/:id/:path
val atomData = QAndAAtom(
typeLabel = "Q&A",
item = QAndAItem(
title = Some("What is a
gravitational wave?"),
body = "I don't know."))
/api/qanda/some-id/item.body
body: "Einstein’s general theory of relativity predicts that
the presence of mass causes a curvature in spacetime."
The Path to Generic Endpoints using Shapeless | @marry16_08
val atomData = QAndAAtom(
typeLabel = "Q&A",
item = QAndAItem(
title = Some("What is a gravitational wave?"),
body = "Einstein’s general theory of relativity
predicts that the presence of mass causes
a curvature in spacetime."))
The Path to Generic Endpoints using Shapeless | @marry16_08
case class Employee(name: String, age: Int, manager: Boolean)
case class IceCream(name: String, numCherries: Int, inCone: Boolean)
String :: Int :: Boolean :: HNil
HList
Generic[IceCream].to(iceCream)
The Path to Generic Endpoints using Shapeless | @marry16_08
Generic[Employee].to(employee)
case class Atom(id: String, atomType: String, data: AtomData)
sealed trait AtomData
object AtomData {
case class QandA(media: QAndAAtom) extends AtomData
}
case class QAndAAtom(typeLabel: Option[String], item: QAndAItem)
case class QAndAItem(title: Option[String], body:String)
val atomData = QAndAAtom(
typeLabel = Some("Q&A"),
item = QAndAItem(
title = Some("What is a gravitational wave?"),
body = "I don't know."))
val qaAtom = Atom("some-id", "QandA", AtomData.QandA(atomData))
The Path to Generic Endpoints using Shapeless | @marry16_08
val genericRepr = LabelledGeneric[Atom].to(qaAtom)
// "some-id" :: "QandA" :: QandA(QandAAtom(...)) :: HNil
val updatedGeneric = genericRepr + (Symbol("atomType") ->> "Updated type"))
// "some-id" :: "Updated type" :: QandA(QandAAtom(...)) :: HNil
val nestedField = "item.title"
val updatedGeneric2 = genericRepr + (Symbol(nestedField) ->> "Updated field"))
// error: Expression scala.Symbol.apply(ScalaFiddle.this.nestedField) does
// not evaluate to a constant or a stable reference value
Atom
id: String
type: String
data: AtomData
qa: QandAAtom
item: QandAItem
typeLabel: Option[String]
title: Option[String]
body: String
The Path to Generic Endpoints using Shapeless | @marry16_08
Convert to nested map: Map[String, Any]
Update the field recursively
The Path to Generic Endpoints using Shapeless | @marry16_08
trait Greetings[A] {
def message(creature: A): String
}
object Greetings {
implicit def humanMessage: Greetings[Human] = new Greetings[Human] {
override def message(human: Human): String = s"${human.name} says hello."
}
implicit def catMessage: Greetings[Cat] = new Greetings[Cat] {
override def message(cat: Cat): String = s"${cat.name} says meow."
}
implicit class GreetingsOps[A](creature: A) {
def greet(implicit greetings: Greetings[A]) = {
greetings.message(creature)
}
}
}
Human("Maria").greet // "Maria says hello."
Cat("Mistoffelees").greet // "Mistoffelees says meow."
Dog("Pollicle").greet // error: could not find implicit value for parameter message
The Path to Generic Endpoints using Shapeless | @marry16_08
trait ToMapRec[L <: HList] {
def apply(l: L): Map[String, Any]
}
object ToMapRec {
implicit def hconsToMapRecOption[K <: Symbol, V, R <: HList, T <: HList]
(implicit
wit: Witness.Aux[K],
gen: LabelledGeneric.Aux[V, R],
tmrT: Lazy[ToMapRec[T]],
tmrH: Lazy[ToMapRec[R]]
): ToMapRec[FieldType[K, Option[V]] :: T] = ???
}
The Path to Generic Endpoints using Shapeless | @marry16_08
trait ToMapRec[L <: HList] {
def apply(l: L): Map[String, Any]
}
...
implicit def hconsToMapRecOption[K <: Symbol, V, R <: HList, T <: HList]
(implicit
wit: Witness.Aux[K],
gen: LabelledGeneric.Aux[V, R],
tmrT: Lazy[ToMapRec[T]],
tmrH: Lazy[ToMapRec[R]]
): ToMapRec[FieldType[K, Option[V]] :: T] = new ToMapRec[FieldType[K, Option[V]] :: T] {
override def apply(l: FieldType[K, Option[V]] :: T): Map[String, Any] = {
tmrT.value(l.tail) + (wit.value.name -> l.head.map(elem => tmrH.value(gen.to(elem))))
}
}
...
val atomMap = atomData.toMap
val atomDataMap = Map(
"item" -> Map(
"title" -> Some("What is a gravitational wave?"),
"body" -> "I don't know."),
"typeLabel" -> "Q&A")
The Path to Generic Endpoints using Shapeless | @marry16_08
trait FromMap[L <: HList] {
def apply(m: Map[String, Any]): Option[L]
}
...
implicit def hconsFromMapOption[K <: Symbol, V, R <: HList, T <: HList]
(implicit
witness: Witness.Aux[K],
gen: LabelledGeneric.Aux[V, R],
fromMapH: Lazy[FromMap[R]],
fromMapT: Lazy[FromMap[T]]
): FromMap[FieldType[K, Option[V]] :: T] = new FromMap[FieldType[K, Option[V]] :: T] {
def apply(m: Map[String, Any]): Option[FieldType[K, Option[V]] :: T] = {
m(witness.value.name) match {
case Some(v) =>
for {
r <- Typeable[Option[Map[String, Any]]].cast(Some(v))
h <- r.map(fromMapH.value(_))
t <- fromMapT.value(m)
} yield field[K](h.map(gen.from)) :: t
case None =>
for {
t <- fromMapT.value(m)
} yield field[K](None) :: t
}
}
}
...
val atomObj = to[Atom.Immutable].from(updatedAtom)
The Path to Generic Endpoints using Shapeless | @marry16_08
The Path to Generic Endpoints using Shapeless | @marry16_08
The Path to Generic Endpoints using Shapeless | @marry16_08
The Path to Generic Endpoints using Shapeless | @marry16_08