Skip to content

Instantly share code, notes, and snippets.

@milessabin
Last active August 29, 2015 14:20
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save milessabin/a212d946aef33811fee1 to your computer and use it in GitHub Desktop.
Save milessabin/a212d946aef33811fee1 to your computer and use it in GitHub Desktop.
Functional update of common fields of a sealed family of case classes via a case-class-like copy through the common super type ...
import shapeless._
/**
* Functional update of common fields of a sealed family of case classes
* via a case-class-like copy through the common super type ...
*/
object BaseCopyDemo extends App {
import copySyntax._
// Sealed family of case classes ...
sealed trait Base
case class Foo(i: Int, b: Boolean) extends Base
case class Bar(i: Int, s: String, b: Boolean) extends Base
case class Baz(i: Int, b: Boolean, d: Double) extends Base
case class Quux(c: Char, i: Int, b: Boolean) extends Base
// case class copy style functional update through the common super-type ...
val b1: Base = Foo(23, true)
assert(b1.copy(i = 13) == Foo(13, true))
val b2: Base = Bar(23, "foo", false)
assert(b2.copy(i = 13, b = true) == Bar(13, "foo", true))
val b3: Base = Baz(23, false, 2.3)
assert(b3.copy(i = 13) == Baz(13, false, 2.3))
val b4: Base = Quux('*', 23, false)
assert(b4.copy(b = true, i = 13) == Quux('*', 13, true))
}
// Implementation in terms of RecordArgs, Generic and Lazy ...
object copySyntax {
class CopySyntax[T](t: T) {
object copy extends RecordArgs {
def applyRecord[R <: HList](r: R)(implicit update: UpdateRepr[T, R]): T = update(t, r)
}
}
implicit def apply[T](t: T): CopySyntax[T] = new CopySyntax[T](t)
}
trait UpdateRepr[T, R <: HList] {
def apply(t: T, r: R): T
}
object UpdateRepr {
import ops.record._
implicit def mergeUpdateRepr[T <: HList, R <: HList]
(implicit merger: Merger.Aux[T, R, T]): UpdateRepr[T, R] =
new UpdateRepr[T, R] {
def apply(t: T, r: R): T = merger(t, r)
}
implicit def cnilUpdateRepr[R <: HList]: UpdateRepr[CNil, R] =
new UpdateRepr[CNil, R] {
def apply(t: CNil, r: R): CNil = t
}
implicit def cconsUpdateRepr[H, T <: Coproduct, R <: HList]
(implicit
uh: Lazy[UpdateRepr[H, R]],
ut: Lazy[UpdateRepr[T, R]]
): UpdateRepr[H :+: T, R] =
new UpdateRepr[H :+: T, R] {
def apply(t: H :+: T, r: R): H :+: T = t match {
case Inl(h) => Inl(uh.value(h, r))
case Inr(t) => Inr(ut.value(t, r))
}
}
implicit def genProdUpdateRepr[T, R <: HList, Repr <: HList]
(implicit
prod: HasProductGeneric[T],
gen: LabelledGeneric.Aux[T, Repr],
update: Lazy[UpdateRepr[Repr, R]]
): UpdateRepr[T, R] =
new UpdateRepr[T, R] {
def apply(t: T, r: R): T = gen.from(update.value(gen.to(t), r))
}
implicit def genCoprodUpdateRepr[T, R <: HList, Repr <: Coproduct]
(implicit
coprod: HasCoproductGeneric[T],
gen: Generic.Aux[T, Repr],
update: Lazy[UpdateRepr[Repr, R]]
): UpdateRepr[T, R] =
new UpdateRepr[T, R] {
def apply(t: T, r: R): T = gen.from(update.value(gen.to(t), r))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment