Skip to content

Instantly share code, notes, and snippets.

@milessabin
Last active December 2, 2020 11:37
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save milessabin/c3c9fbc57b1d913c2ee4 to your computer and use it in GitHub Desktop.
Save milessabin/c3c9fbc57b1d913c2ee4 to your computer and use it in GitHub Desktop.
Merging arbitrary case classes via shapeless ... (LabelledGeneric and record merge).
import shapeless._
object CaseClassMergeDemo extends App {
import mergeSyntax._
case class Foo(i: Int, s: String, b: Boolean)
case class Bar(b: Boolean, s: String)
val foo = Foo(23, "foo", true)
val bar = Bar(false, "bar")
val merged = foo merge bar
assert(merged == Foo(23, "bar", false))
}
// Implementation in terms of LabelledGeneric ...
object mergeSyntax {
implicit class MergeSyntax[T](t: T) {
def merge[U](u: U)(implicit merge: CaseClassMerge[T, U]): T = merge(t, u)
}
}
trait CaseClassMerge[T, U] {
def apply(t: T, u: U): T
}
object CaseClassMerge {
import ops.record.Merger
def apply[T, U](implicit merge: CaseClassMerge[T, U]): CaseClassMerge[T, U] = merge
implicit def mkCCMerge[T, U, RT <: HList, RU <: HList]
(implicit
tgen: LabelledGeneric.Aux[T, RT],
ugen: LabelledGeneric.Aux[U, RU],
merger: Merger.Aux[RT, RU, RT]
): CaseClassMerge[T, U] =
new CaseClassMerge[T, U] {
def apply(t: T, u: U): T =
tgen.from(merger(tgen.to(t), ugen.to(u)))
}
}
@lambdista
Copy link

Is there a way to write a merger that just ignores fields of the rhs class that are not present in the lhs class? That is, make the following example work:

case class Foo(a: String, b: Int, c: String)

case class Bar(b: Int, d: String)

val foo = Foo("hello", 42, "world")
val bar = Bar(1, "bar")

val merged = foo merge bar
assert(merged == Foo("hello", 1, "world"))

@jwien001
Copy link

jwien001 commented Mar 3, 2020

@lambdista Did you ever figure out how to do what you're asking?

@lambdista
Copy link

No, to tell the truth I asked out of curiosity when I started studying shapeless but didn't need to accomplish that task so I didn't delve into accomplishing that.

@jwien001
Copy link

jwien001 commented Mar 3, 2020

No worries. Thanks for getting back to me and not pulling a https://xkcd.com/979/.

@lambdista
Copy link

lol, you're welcome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment