Skip to content

Instantly share code, notes, and snippets.

@ahoy-jon
Created July 17, 2019 21:37
Show Gist options
  • Save ahoy-jon/3e9d9a63f9d2141a35d5b9e79f9f1ece to your computer and use it in GitHub Desktop.
Save ahoy-jon/3e9d9a63f9d2141a35d5b9e79f9f1ece to your computer and use it in GitHub Desktop.
implicit val qTree:DeepEqual[QTree[Unit]] = DeepEqual.anyDeepEqual
//DeepEqual.gen[DescribeLong]
DeepEqual.gen[ParkaAnalysis].explainNotEqual(pa,paFromJson).foreach(println)
assert(pa == paFromJson)
}
}
case class Element(key: String, value: Long)
case class Element2(key: String, value: Long, eulav: Long)
trait DeepEqual[T] {
def explainNotEqual(t1:T,t2:T):Seq[String]
}
trait LowPriorityDeepEqual {
def anyDeepEqual[T]:DeepEqual[T] = new DeepEqual[T] {
override def explainNotEqual(t1: T, t2: T): Seq[String] = if(t1 == t2)
Nil
else Seq(s"left : $t1\nright : $t2")
}
}
object DeepEqual extends LowPriorityDeepEqual{
implicit def str: Typeclass[String] = anyDeepEqual
implicit def long: Typeclass[Long] = anyDeepEqual
implicit def optionDeepEqual[V:DeepEqual]:DeepEqual[Option[V]] = new Typeclass[Option[V]] {
override def explainNotEqual(t1: Option[V], t2: Option[V]): Seq[String] = {
(t1, t2) match {
case (Some(x),Some(y)) => implicitly[DeepEqual[V]].explainNotEqual(x,y)
case (None,None) => Nil
case (Some(x), None) => Seq(s"left : extra value $x")
case (None, Some(y)) => Seq(s"right : extra value $y")
}
}
}
implicit def mapDeepEqual[K,V:DeepEqual]:DeepEqual[Map[K,V]] = new Typeclass[Map[K,V]] {
override def explainNotEqual(t1: Map[K, V], t2: Map[K, V]): Seq[String] = {
val keys: Seq[K] = (t1.keySet ++ t2.keySet).toSeq
keys.flatMap(k => {
(t1.get(k),t2.get(k)) match {
case (Some(v1),Some(v2)) => implicitly[DeepEqual[V]].explainNotEqual(v1,v2)
case (Some(v1), None) => Seq(s"left : extra value at key $k : $v1")
case (None,Some(v2)) => Seq(s"right : extra value at key $k : $v2")
case _ => Nil
}
})
}
}
implicit def seqDeepEqual[T:DeepEqual]:DeepEqual[Seq[T]] = new Typeclass[Seq[T]] {
private val deepEqual = implicitly[Typeclass[T]]
override def explainNotEqual(t1: Seq[T], t2: Seq[T]): Seq[String] = {
if(t1 == t2) Nil
else {
val min = Math.min(t1.size,t2.size)
val max = Math.max(t1.size,t2.size)
val common: Seq[String] = t1.take(min).zip(t2.take(min)).flatMap({
case (x,y) =>
deepEqual.explainNotEqual(x,y)
})
val extra: Seq[String] = if(max == min) Nil else {
if(t1.size > t2.size) t1.drop(min).map(x => s"left : extra element : $x")
else t2.drop(min).map(x => s"right : extra element : $x")
}
common ++ extra
}
}
}
def explainNotEqual[T:DeepEqual](t1:T,t2:T):Seq[String] = {
implicitly[DeepEqual[T]].explainNotEqual(t1,t2)
}
import magnolia._
type Typeclass[T] = DeepEqual[T]
def combine[T](caseClass: CaseClass[Typeclass, T]): Typeclass[T] = new Typeclass[T] {
override def explainNotEqual(t1: T, t2: T): Seq[String] = if(t1 == t2) Nil else {
caseClass.parameters.flatMap(param => {
param.typeclass.explainNotEqual(param.dereference(t1), param.dereference(t2))
})
}
}
def dispatch[T](sealedTrait: SealedTrait[Typeclass, T]): Typeclass[T] = new Typeclass[T] {
override def explainNotEqual(t1: T, t2: T): Seq[String] = {
sealedTrait.dispatch(t1)(handle1 => {
sealedTrait.dispatch(t2)(handle2 => {
if(handle1.typeName == handle2.typeName) {
handle1.typeclass.explainNotEqual(handle1.cast(t1),handle1.cast(t2))
} else {
anyDeepEqual[Any].explainNotEqual(t1,t2)
}
})
})
}
}
implicit def gen[T]: Typeclass[T] = macro Magnolia.gen[T]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment