Instantly share code, notes, and snippets.

# tonymorris/Equal.scala Last active Dec 10, 2015

What would you like to do?
Equal library for Scala (standalone)
 trait Equal[A] { def equal(a1: A, a2: A): Boolean def apply(a1: A, a2: A): Boolean = equal(a1, a2) def contramap[B](f: B => A): Equal[B] = Equal((b1, b2) => apply(f(b1), f(b2))) def &&[B](b: => Equal[B]): Equal[(A, B)] = Equal((ab1, ab2) => apply(ab1._1, ab2._1) && b(ab1._2, ab2._2)) def ||[B](b: => Equal[B]): Equal[(A, B)] = Equal((ab1, ab2) => apply(ab1._1, ab2._1) || b(ab1._2, ab2._2)) def ==>[B](b: => Equal[B]): Equal[(A, B)] = Equal((ab1, ab2) => !apply(ab1._1, ab2._1) || b(ab1._2, ab2._2)) def <==[B](b: => Equal[B]): Equal[(A, B)] = Equal((ab1, ab2) => apply(ab1._1, ab2._1) || !b(ab1._2, ab2._2)) def ===[B](b: Equal[B]): Equal[(A, B)] = Equal((ab1, ab2) => apply(ab1._1, ab2._1) == b(ab1._2, ab2._2)) def =/=[B](b: Equal[B]): Equal[(A, B)] = negate === b def negate: Equal[A] = Equal((a1, a2) => !apply(a1, a2)) def unary_! : Equal[A] = negate def zip[B](b: => Equal[B]): Equal[(A, B)] = &&(b) def notEqual(a1: A, a2: A): Boolean = negate(a1, a2) def curry: A => A => Boolean = a1 => a2 => apply(a1, a2) def curryFlip: A => A => Boolean = a2 => a1 => apply(a1, a2) def run: (A, A) => Boolean = (a1, a2) => apply(a1, a2) def when1(a1: A, a2: A): Option[A] = if(apply(a1, a2)) Some(a1) else None def when2(a1: A, a2: A): Option[A] = when1(a2, a1) def unless1(a1: A, a2: A): Option[A] = negate.when1(a1, a2) def unless2(a1: A, a2: A): Option[A] = negate.when2(a1, a2) def pick1(a1: A, a2: A): A = if(apply(a1, a2)) a1 else a2 def pick2(a1: A, a2: A): A = negate.pick1(a1, a2) } object Equal extends EqualFunctions with EqualInstances { def apply[A](f: (A, A) => Boolean): Equal[A] = new Equal[A] { def equal(a1: A, a2: A) = f(a1, a2) } def always[A](p: => Boolean): Equal[A] = apply((_, _) => p) def for1[A](f: A => Boolean): Equal[A] = apply((a1, _) => f(a1)) def for2[A](f: A => Boolean): Equal[A] = apply((_, a2) => f(a2)) def curried[A](f: A => A => Boolean): Equal[A] = apply((a1, a2) => f(a1)(a2)) def counzip[A, B](e: Either[Equal[A], Equal[B]]): Equal[Either[A, B]] = e match { case Left(ea) => Equal((a1, a2) => a1.left exists (aa1 => a2.left exists (aa2 => ea(aa1, aa2)))) case Right(eb) => Equal((b1, b2) => b1.right exists (bb1 => b2.right exists (bb2 => eb(bb1, bb2)))) } } trait EqualFunctions { implicit def ToEqualed[A](a: A): Equaled[A] = Equaled(a) case class EqualingBy[A](a1: A, a2: A) { def by[B](f: A => B)(implicit E: Equal[B]): Boolean = E contramap f apply (a1, a2) } case class NotEqualingBy[A](a1: A, a2: A) { def by[B](f: A => B)(implicit E: Equal[B]): Boolean = E contramap f notEqual (a1, a2) } case class Equaled[A](a: A) { def ===(a2: A)(implicit E: Equal[A]): Boolean = E(a, a2) def =/=(a2: A)(implicit E: Equal[A]): Boolean = E.notEqual(a, a2) def >===(a2: A): EqualingBy[A] = EqualingBy(a, a2) def >=/=(a2: A): NotEqualingBy[A] = NotEqualingBy(a, a2) def equal1(implicit E: Equal[A]): A => Boolean = E.curry(a) def equal2(implicit E: Equal[A]): A => Boolean = E.curryFlip(a) def when1(a2: A)(implicit E: Equal[A]): Option[A] = E.when1(a, a2) def when2(a2: A)(implicit E: Equal[A]): Option[A] = E.when2(a, a2) def unless1(a2: A)(implicit E: Equal[A]): Option[A] = E.unless1(a, a2) def unless2(a2: A)(implicit E: Equal[A]): Option[A] = E.when1(a, a2) def pick1(a2: A)(implicit E: Equal[A]): A = E.pick1(a, a2) def pick2(a2: A)(implicit E: Equal[A]): A = E.pick2(a, a2) } } trait EqualInstances { // CAUTION def objectEquals[A]: Equal[A] = new Equal[A] { def equal(a1: A, a2: A): Boolean = a1 == a2 } // Scala primitives implicit val BooleanEqual: Equal[Boolean] = objectEquals implicit val ByteEqual: Equal[Byte] = objectEquals implicit val CharEqual: Equal[Char] = objectEquals implicit val DoubleEqual: Equal[Double] = objectEquals implicit val FloatEqual: Equal[Float] = objectEquals implicit val IntEqual: Equal[Int] = objectEquals implicit val LongEqual: Equal[Long] = objectEquals implicit val NothingEqual: Equal[Nothing] = Equal.always(true) implicit val ShortEqual: Equal[Short] = objectEquals implicit val UnitEqual: Equal[Unit] = Equal.always(true) // Java primitives implicit val JavaBooleanEqual: Equal[java.lang.Boolean] = objectEquals implicit val JavaByteEqual: Equal[java.lang.Byte] = objectEquals implicit val JavaCharEqual: Equal[java.lang.Character] = objectEquals implicit val JavaDoubleEqual: Equal[java.lang.Double] = objectEquals implicit val JavaFloatEqual: Equal[java.lang.Float] = objectEquals implicit val JavaIntEqual: Equal[java.lang.Integer] = objectEquals implicit val JavaLongEqual: Equal[java.lang.Long] = objectEquals implicit val JavaShortEqual: Equal[java.lang.Short] = objectEquals // big numbers implicit val BigDecimalEqual: Equal[BigDecimal] = objectEquals implicit val BigIntegerEqual: Equal[BigInt] = objectEquals // products implicit def Function0Equal[A: Equal]: Equal[Function0[A]] = implicitly[Equal[A]].contramap(_()) implicit def Tuple2Equal[A: Equal, B: Equal]: Equal[(A, B)] = implicitly[Equal[A]] && implicitly[Equal[B]] implicit def Tuple3Equal[A: Equal, B: Equal, C: Equal]: Equal[(A, B, C)] = implicitly[Equal[A]] && implicitly[Equal[B]] && implicitly[Equal[C]] contramap { case (a, b, c) => ((a, b), c) } implicit def Tuple4Equal[A: Equal, B: Equal, C: Equal, D: Equal]: Equal[(A, B, C, D)] = implicitly[Equal[A]] && implicitly[Equal[B]] && implicitly[Equal[C]] && implicitly[Equal[D]] contramap { case (a, b, c, d) => (((a, b), c), d) } implicit def Tuple5Equal[A: Equal, B: Equal, C: Equal, D: Equal, E: Equal]: Equal[(A, B, C, D, E)] = implicitly[Equal[A]] && implicitly[Equal[B]] && implicitly[Equal[C]] && implicitly[Equal[D]] && implicitly[Equal[E]] contramap { case (a, b, c, d, e) => ((((a, b), c), d), e) } // sums implicit def OptionEqual[A: Equal]: Equal[Option[A]] = Equal((x1, x2) => x1 match { case None => x2.isEmpty case Some(a1) => x2 exists (implicitly[Equal[A]].curry(a1)) }) implicit def EitherEqual[A: Equal, B: Equal]: Equal[Either[A, B]] = Equal((x1, x2) => x1 match { case Left(a1) => x2.left exists (implicitly[Equal[A]].curry(a1)) case Right(b1) => x2.right exists (implicitly[Equal[B]].curry(b1)) }) implicit def ListEqual[A: Equal]: Equal[List[A]] = Equal((x1, x2) => (x1 corresponds x2)(implicitly[Equal[A]].run)) implicit def StreamEqual[A: Equal]: Equal[Stream[A]] = Equal((x1, x2) => (x1 corresponds x2)(implicitly[Equal[A]].run)) // collections implicit def QueueEqual[A: Equal]: Equal[collection.immutable.Queue[A]] = Equal((x1, x2) => (x1 corresponds x2)(implicitly[Equal[A]].run)) implicit def StackEqual[A: Equal]: Equal[collection.immutable.Stack[A]] = Equal((x1, x2) => (x1 corresponds x2)(implicitly[Equal[A]].run)) // strings implicit val StringEqual: Equal[String] = objectEquals }