Created
October 23, 2015 07:44
-
-
Save crakjie/51ab7435f5b0ac3e669e to your computer and use it in GitHub Desktop.
This file contains hidden or 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
package com.misterbell.shed.models.travellers | |
import shapeless.ops.record.Merger | |
import shapeless._ | |
object OpenBaseCopyDemo extends App { | |
import openCopySyntax._ | |
import mergeSyntax._ | |
// Open family of case classes ... | |
// Parm T is used to return T and not just Base. | |
trait Base[T <: Base[T]] extends OpenFamily[T] { | |
val i: Int | |
val b: Option[Boolean] | |
case class BaseFields(i: Int, b: Option[Boolean]) | |
def baseFields = BaseFields(i, b) | |
def baseCopy(base: BaseFields): T //Here it's return T | |
} | |
case class Foo(i: Int, b: Option[Boolean]) extends Base[Foo] { | |
def baseCopy(base: BaseFields) = this merge base | |
} | |
case class Bar(i: Int, s: String, b: Option[Boolean]) extends Base[Bar] { | |
def baseCopy(base: BaseFields) = this merge base | |
} | |
case class Baz(i: Int, b: Option[Boolean], d: Double) extends Base[Baz] { | |
def baseCopy(base: BaseFields) = this merge base | |
} | |
case class Quux(c: Char, i: Int, b: Option[Boolean]) extends Base[Quux] { | |
def baseCopy(base: BaseFields) = this merge base | |
} | |
// case class copy style functional update through the common super-type ... | |
val b1: Foo = Foo(23, Some(true)) | |
val x1 : Foo = b1.copy(i = 13) | |
assert(b1.copy(i = 13) == Foo(13, Some(true))) | |
val b2: Bar = Bar(23, "foo", Some(false)) | |
assert(b2.copy(i = 13, b = Some(true)) == Bar(13, "foo", Some(true))) | |
val b3: Baz = Baz(23, Some(false), 2.3) | |
assert(b3.copy(i = 13) == Baz(13, Some(false), 2.3)) | |
val b4: Quux = Quux('*', 23, Some(false)) | |
assert(b4.copy(b = Some(true), i = 13) == Quux('*', 13, Some(true))) | |
/* Because it's return T this method should compile */ | |
/* And it's do when b is Boolean */ | |
/* But's it's dosen't compile when the Boolean is contain in Option*/ | |
/* | |
OpenCopy.scala:53: could not find implicit value for parameter update: com.misterbell.shed.models.travellers.UpdateRepr[t.BaseFields,shapeless.::[shapeless.labelled.FieldType[shapeless.tag.@@[Symbol,String("b")],Some[Boolean]],shapeless.HNil]] | |
[error] error after rewriting to openCopySyntax.apply[T](t).copy.applyDynamicNamed("apply")(scala.Tuple2("b", Some(true))) | |
[error] possible cause: maybe a wrong Dynamic method signature? | |
[error] t.copy(b = Some(true)) | |
*/ | |
def a[T <: Base[T]](t : T): T = { | |
t.copy(b = Some(true)) | |
} | |
} | |
object openCopySyntax { | |
class CopySyntax[T, BaseFields0](t: OpenFamily[T] { type BaseFields = BaseFields0 }) { | |
object copy extends RecordArgs { | |
def applyRecord[R <: HList](r: R)(implicit update: UpdateRepr[BaseFields0, R]): T = | |
t.baseCopy(update(t.baseFields, r)) | |
} | |
} | |
implicit def apply[T](t: OpenFamily[T]): CopySyntax[T, t.BaseFields] = new CopySyntax(t) | |
} | |
trait OpenFamily[T] { | |
type BaseFields | |
def baseFields: BaseFields | |
def baseCopy(base: BaseFields): 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)) | |
} | |
} | |
// 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))) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment