Skip to content

Instantly share code, notes, and snippets.

@akozhemiakin
Last active December 8, 2016 18:43
Show Gist options
  • Save akozhemiakin/3eb77e59c653f67ab982ba8c028b178c to your computer and use it in GitHub Desktop.
Save akozhemiakin/3eb77e59c653f67ab982ba8c028b178c to your computer and use it in GitHub Desktop.
import shapeless.labelled._
import shapeless.ops.hlist
import shapeless.{HList, HNil, LabelledGeneric}
trait Updater[A, B] {
def apply(a: A, b: B): A
}
object Updater {
def apply[A, B](implicit updater: Updater[A, B]): Updater[A, B] = updater
def update[A, B](a: A, b: B)(implicit updater: Updater[A, B]): A = updater(a, b)
implicit def updater[A, ARepr <: HList, B, BRepr <: HList](
implicit
aGen: LabelledGeneric.Aux[A, ARepr],
bGen: LabelledGeneric.Aux[B, BRepr],
multiReplacer: MultiReplacer[ARepr, BRepr]
): Updater[A, B] = new Updater[A, B] {
override def apply(a: A, b: B): A = aGen.from(multiReplacer(
aGen.to(a),
bGen.to(b)
))
}
trait MultiReplacer[A <: HList, B <: HList] {
def apply(a: A, b: B): A
}
implicit def multiReplacerHNil[A <: HList, B <: HNil]: MultiReplacer[A, B] = new MultiReplacer[A, B] {
override def apply(a: A, b: B): A = a
}
implicit def multiReplacerHList[A <: HList, B <: HList, OA, K, V, VA, BH, BT <: HList] (
implicit
ev1: hlist.IsHCons.Aux[B, BH, BT],
ev2: BH <:< FieldType[K, V],
ev3: V <:< Option[VA],
replacer: hlist.Replacer.Aux[A, FieldType[K, VA], FieldType[K, VA], (FieldType[K, VA], A)],
multiReplacer: MultiReplacer[A, BT]
): MultiReplacer[A, B] = new MultiReplacer[A, B] {
override def apply(a: A, b: B): A = multiReplacer(
b.head.asInstanceOf[Option[VA]] match {
case Some(x) => replacer(a, field[K](x))._2
case _ => a
},
b.tail
)
}
}
///
/// It can be used like this:
///
case class User(id: Int, name: String, country: String)
case class UserPatch(name: Option[String] = None, country: Option[String] = None)
val user = User(10, "Artyom", "Russia")
val userPatch1 = UserPatch(name = Some("John"), country = Some("USA"))
val userPatch2 = UserPatch(country = Some("USA"))
val userPatch3 = UserPatch(name = Some("John"))
Updater.update(user, userPatch1)
// User(10,John,USA)
Updater.update(user, userPatch2)
// User(10,Artyom,USA)
Updater.update(user, userPatch3)
// User(10,John,Russia)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment