Last active
December 8, 2016 18:43
-
-
Save akozhemiakin/3eb77e59c653f67ab982ba8c028b178c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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