Skip to content

Instantly share code, notes, and snippets.

@sergeykolbasov
Last active November 19, 2016 21:42
Show Gist options
  • Save sergeykolbasov/0022a663d3bf83bfbb7a63021447b6d8 to your computer and use it in GitHub Desktop.
Save sergeykolbasov/0022a663d3bf83bfbb7a63021447b6d8 to your computer and use it in GitHub Desktop.
import shapeless.{::, DepFn1, HList, HNil, LabelledGeneric}
import shapeless.labelled.{FieldType, field}
object deepcopy extends App {
trait DeepCopy[A] extends DepFn1[A]
object DeepCopy extends LowPriorityDeepCopy {
type Aux[A, O] = DeepCopy[A] {type Out = O}
/**
* For ends of both lists just return [[HNil]]
*
* @return
*/
implicit def hnilCopy: Aux[HNil, HNil] = new DeepCopy[HNil] {
override type Out = HNil
override def apply(t: HNil): Out = HNil
}
/**
* Case when current element under cursor has different [[LabelledGeneric]] representation from the same element in type B
*
* Try to get instance of [[DeepCopy]] for those representations (it could be recursive until it would be resolved by [[mkCopyEqualStruct]])
* Then run deepCopy for those elements, get B HList representation as result, build element from type B and put it into field with same name
*
* @param aLg [[LabelledGeneric]] of head element of A HList into some HList
* @param bLg [[LabelledGeneric]] of head element of B HList into another HList
* @param headCopy instance of [[DeepCopy]] to run for representation of aLg converting it into acceptable by bLg HList representation
* @param tailCopy instance of [[DeepCopy]] for rest of elements. In fact - it's a recursive implicit resolution until we reach HNil
* @tparam Id name of field for head element in A and B (we assume that they are equal)
* @tparam AH type of head element in A HList representation
* @tparam BH type of head element in B HList representation
* @tparam AT tail of A HList representation
* @tparam BT tail of B HList representation
* @tparam AR Resulting type of aLg. In fact - it's an HList that we will try to convert into BR using headCopy
* @tparam BR Resulting type of bLg.
*/
implicit def mkCopyDiffStruct[Id, AH, BH, AT <: HList, BT <: HList, AR <: HList, BR <: HList](implicit
aLg: LabelledGeneric.Aux[AH, AR],
bLg: LabelledGeneric.Aux[BH, BR],
headCopy: DeepCopy.Aux[AR, BR],
tailCopy: DeepCopy.Aux[AT, BT]
): DeepCopy.Aux[FieldType[Id, AH] :: AT, FieldType[Id, BH] :: BT] = {
new DeepCopy[FieldType[Id, AH] :: AT] {
override type Out = FieldType[Id, BH] :: BT
override def apply(t: FieldType[Id, AH] :: AT): Out = {
val head = aLg.to(t.head: AH)
field[Id](bLg.from(headCopy(head))) :: tailCopy(t.tail)
}
}
}
}
trait LowPriorityDeepCopy {
/**
* Case when current element under cursor has same [[LabelledGeneric]] representation as same element in type B
*
* @param aLg [[LabelledGeneric]] of head element of A HList into some HList
* @param bLg [[LabelledGeneric]] of head element of A HList into the same HList
* @param tailCopy instance of [[DeepCopy]] for rest of elements. In fact - it's a recursive implicit resolution until we reach HNil
* @tparam Id name of field for head element in A and B (we assume that they are equal)
* @tparam AH type of head element in A HList representation
* @tparam BH type of head element in B HList representation
* @tparam AT tail of A HList representation
* @tparam BT tail of B HList representation
* @tparam Repr Resulting type of aLg and bLg.
* @return
*/
implicit def mkCopyEqualStruct[Id, AH, BH, AT <: HList, BT <: HList, Repr <: HList](implicit
aLg: LabelledGeneric.Aux[AH, Repr],
bLg: LabelledGeneric.Aux[BH, Repr],
tailCopy: DeepCopy.Aux[AT, BT]
): DeepCopy.Aux[FieldType[Id, AH] :: AT, FieldType[Id, BH] :: BT] = {
new DeepCopy[FieldType[Id, AH] :: AT] {
override type Out = FieldType[Id, BH] :: BT
override def apply(t: FieldType[Id, AH] :: AT): Out = {
val repr = aLg.to(t.head: AH)
field[Id](bLg.from(repr)) :: tailCopy(t.tail)
}
}
}
/**
* For primitive types we have no [[LabelledGeneric]]. Just straight copy
*/
implicit def mkCopyEqualPrimitives[Id, H, AT <: HList, BT <: HList](implicit
tailCopy: DeepCopy.Aux[AT, BT]
): DeepCopy.Aux[FieldType[Id, H] :: AT, FieldType[Id, H] :: BT] = {
new DeepCopy[FieldType[Id, H] :: AT] {
override type Out = FieldType[Id, H] :: BT
override def apply(t: FieldType[Id, H] :: AT): Out = {
field[Id](t.head: H) :: tailCopy(t.tail)
}
}
}
implicit def initDeepCopy[A, B, AR <: HList, BR <: HList](implicit
aLg: LabelledGeneric.Aux[A, AR],
bLg: LabelledGeneric.Aux[B, BR],
deepCopy: DeepCopy.Aux[AR, BR]
): DeepCopy.Aux[A, B] = {
new DeepCopy[A] {
type Out = B
override def apply(t: A): B = {
bLg from deepCopy(aLg.to(t))
}
}
}
}
implicit class ApplyDeepCopy[A](a: A) {
def convert[B](implicit deepCopy: DeepCopy.Aux[A, B]) = deepCopy(a)
}
case class A(i: Int)
case class B(a: A)
case class C(b: B, i: Int)
case class A2(i: Int)
case class B2(a: A2)
case class C2(b: B2, i: Int)
println(C2(B2(A2(123)), 42).convert[C])
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment