Skip to content

Instantly share code, notes, and snippets.

@robhinds
Created October 22, 2018 21:03
Show Gist options
  • Save robhinds/73c6200b4deffffb4e3bdd2fa2c725f1 to your computer and use it in GitHub Desktop.
Save robhinds/73c6200b4deffffb4e3bdd2fa2c725f1 to your computer and use it in GitHub Desktop.
import org.scalatest.{FunSpec, Matchers}
import shapeless._
sealed trait Parent {
def description: Option[String]
def another: Option[String]
}
case class B(description: Option[String], another: Option[String]) extends Parent
case class A(description: Option[String], another: Option[String]) extends Parent
sealed trait Updater[T] {
def update(target: T, newVal: Option[String]): T
}
trait GenericUpdater {
val p: Path[_]
def update[T](target: T, newVal: Option[String])(implicit u: Updater[T]): T = u.update(target, newVal)
lazy implicit val cnilUpdater: Updater[CNil] = new Updater[CNil] {
override def update(target: CNil, newVal: Option[String]): CNil = target
}
implicit def genericCoproductUpdater[Head, Tail <: Coproduct](
implicit tailUpdater: Updater[Tail],
mkLens: p.Lens[Head, Option[String]]) = new Updater[Head :+: Tail] {
override def update(target: Head :+: Tail, newVal: Option[String]) = {
target match {
case Inl(head) => Inl(mkLens().set(head)(newVal))
case Inr(tail) => Inr(tailUpdater.update(tail, newVal))
}
}
}
implicit def sealedTraitToGenericUpdater[A, Repr <: Coproduct](
implicit generic: Generic.Aux[A, Repr],
genericUpdater: Updater[Repr]): Updater[A] =
new Updater[A] {
override def update(target: A, newVal: Option[String]): A = generic.from(
genericUpdater.update(generic.to(target), newVal)
)
}
implicit def caseClassToGenericUpdater[A, Repr <: HList](
implicit generic: Generic.Aux[A, Repr],
mkLens: p.Lens[A, Option[String]]
): Updater[A] =
new Updater[A] {
override def update(target: A, newVal: Option[String]): A = mkLens().set(target)(newVal)
}
}
object DescriptionUpdater extends GenericUpdater {
override val p = ^.description
}
object AnotherUpdater extends GenericUpdater {
override val p = ^.another
}
class LensTest extends FunSpec with Matchers {
describe("ordering problems with lenses") {
it("trying to update the first child class alphabetically") {
import DescriptionUpdater._
val parent: Parent = A(Some("not updated"), Some("another"))
val result= update(parent, Some("updated"))
println(result)
result.description shouldBe Some("updated")
}
it("trying to update the second child class alphabetically") {
import DescriptionUpdater._
val parent: Parent = B(Some("not updated"), Some("another"))
val result= update(parent, Some("updated"))
println(result)
result.description shouldBe Some("updated")
}
it("trying to update a specific case class") {
import DescriptionUpdater._
val b: B = B(Some("not updated"), Some("another"))
val result= update(b, Some("updated"))
println(result)
result.description shouldBe Some("updated")
}
it("trying to update different path") {
import AnotherUpdater._
val b: Parent = B(Some("not updated"), Some("another"))
val result= update(b, Some("updated another"))
println(result)
result.another shouldBe Some("updated another")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment