Last active
November 19, 2016 21:42
-
-
Save sergeykolbasov/0022a663d3bf83bfbb7a63021447b6d8 to your computer and use it in GitHub Desktop.
This file contains 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.{::, 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