Last active
December 10, 2015 19:28
-
-
Save tonymorris/4481469 to your computer and use it in GitHub Desktop.
Equal library for Scala (standalone)
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
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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment