Download - Equality For All!
![Page 1: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/1.jpg)
Equality For All!Scala by the Bay, 2014
Bill VennersArtima, Inc.
Escalate Software
Saturday, August 9, 2014
![Page 2: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/2.jpg)
Equality: An equivalence relation with one element per equivalence class
42 4341
42 = 4241 = 41 43 = 43
reflexive: x = x
symmetric: x = y iff y = x
transitive: if x = y and y = z then x = z
Saturday, August 9, 2014
![Page 3: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/3.jpg)
How do I say forty two in code?Let me count the ways...
42
42L
42.0
42.0F
42.toShort
'*'
42.toByte BigInt(42)
BigDecimal(42)
new java.lang.Integer(42)
new java.lang.Long(42L)
new java.lang.Double(42.0)
new java.lang.Float(42.0F) new java.lang.Short(42.toShort)
new java.lang.Character(42)
new java.lang.Byte(42.toByte)
new java.math.BigInteger("42")
new java.math.BigDecimal(42) Complex(42.0, 0.0)
DigitString("42") DigitString("042")
Saturday, August 9, 2014
![Page 4: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/4.jpg)
The equals method implements an equivalence relation on non-null object references:• It is reflexive: for any non-null reference value x, x.equals(x) should return true.• It is symmetric: for any non-null reference values x and y, x.equals(y) should return true
if and only if y.equals(x) returns true.• It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true
and y.equals(z) returns true, then x.equals(z) should return true.• It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
• For any non-null reference value x, x.equals(null) should return false.
Java's equals contract
public boolean equals(Object obj)
final def == (other: Any): Boolean
Saturday, August 9, 2014
![Page 5: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/5.jpg)
scala> 42 == 42Lres0: Boolean = true
scala> BigDecimal(42) == '*'res1: Boolean = true
scala> 42.0F == BigInt(42)res2: Boolean = true
scala> new java.lang.Float(42.0F) == 42.toShortres3: Boolean = true
Co-operative Equality Between Types
Saturday, August 9, 2014
![Page 6: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/6.jpg)
scala> import scala.collection.mutableimport scala.collection.mutable
scala> Set(BigInt(4), BigInt(2)) == mutable.Set(4.toByte, 2.toByte)res4: Boolean = true
scala> Vector(Left(4), Right(2)) == List(Left(4L), Right(2L))res5: Boolean = true
scala> List(mutable.Set(Map(Some(4L) -> BigInt(2)))) == Vector(Set(mutable.Map(Some(4.0) -> new java.lang.Long(2L))))res6: Boolean = true
Co-operative Equality Between Types
Saturday, August 9, 2014
![Page 7: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/7.jpg)
scala> Array(4, 2) == Array(4, 2)res7: Boolean = false
scala> <forty><two></two></forty> == <forty> <two></two> </forty>res8: Boolean = false
scala> new java.math.BigDecimal("42.0") == new java.math.BigDecimal("42.00")res9: Boolean = false
scala> 42.0 == 41.9999999999res10: Boolean = false
scala> "case" == "CASE"res11: Boolean = false
Wanted: Alternate equalities
Saturday, August 9, 2014
![Page 8: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/8.jpg)
scala> "forty two" == 42res19: Boolean = false
scala> List(mutable.Set(Map(Some(4L) -> BigInt(2)))) == Vector(Set(mutable.Map(Some("4.0") -> new java.lang.Long(2L))))res23: Boolean = false
Wanted: Type errors
But how to decide which comparisons compile?
Saturday, August 9, 2014
![Page 9: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/9.jpg)
scala> "forty two" == 42<console>:20: error: types String and Int do not adhere to the type constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.EqualityConstraint[String,Int] "forty two" === 42 ^
scala> BigInt(42) === BigDecimal(42)res1: Boolean = true
Fail to compile if L can never equal R.
Saturday, August 9, 2014
![Page 10: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/10.jpg)
scala> Vector.empty === List.empty<console>:20: error: ambiguous implicit values: both method conflictingEmptySeqConstraint1 in object EqualityConstraint of type [LSEQ[e] <: scala.collection.GenSeq[e], RSEQ[e] <: scala.collection.GenSeq[e]]=> org.scalactic.EqualityConstraint[LSEQ[Nothing],RSEQ[Nothing]] and method conflictingEmptySeqConstraint2 in object EqualityConstraint of type [LSEQ[e] <: scala.collection.GenSeq[e], RSEQ[e] <: scala.collection.GenSeq[e]]=> org.scalactic.EqualityConstraint[LSEQ[Nothing],RSEQ[Nothing]] match expected type org.scalactic.EqualityConstraint[scala.collection.immutable.Vector[A],List[Nothing]] Vector.empty === List.empty ^
scala> Vector.empty === List.empty[Int]res3: Boolean = true
scala> Vector.empty[String] === List.emptyres4: Boolean = true
Fail to compile if L will always equal R.
Saturday, August 9, 2014
![Page 11: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/11.jpg)
Candidate rule:
To compile, an equality comparison must be interesting: values of types L
and R can be either equal or unequal.
Saturday, August 9, 2014
![Page 12: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/12.jpg)
scala> case class Complex(real: Double, imaginary: Double)defined class Complex
scala> implicit def convertIntToComplex(i: Int): Complex = Complex(i, 0.0)convertIntToComplex: (i: Int)Complex
scala> 42 === Complex(42, 0)<console>:24: error: types Int and Complex do not adhere to the type constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.EqualityConstraint[Int,Complex] 42 === Complex(42, 0) ^
scala> Complex(42, 0) === 42<console>:24: error: types Complex and Int do not adhere to the type constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.EqualityConstraint[Complex,Int] Complex(42, 0) === 42 ^
What about implicit conversions?
Saturday, August 9, 2014
![Page 13: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/13.jpg)
scala> implicit val enabler = EqualityEnabledBetween[Int, Complex]enabler: org.scalactic.EqualityEnabledBetween[Int,Complex] = org.scalactic.EqualityEnabledBetween@e5d2d9b
scala> 42 === Complex(42, 0)res2: Boolean = true
scala> Complex(42, 0) === 42res3: Boolean = true
scala> new AnyShouldWrapper(1) === 1 // Probably shouldn't enable...
Intuition: enable some but not all
But what would the rule be?Saturday, August 9, 2014
![Page 14: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/14.jpg)
OK if the conversion is an injection
scala> case class DigitString(digits: String) { | val toInt: Int = digits.toInt | }defined class DigitString
scala> implicit def convert(d: DigitString): Int = | d.digits.toIntconvertDigitStringToInt: (d: DigitString)Int
scala> DigitString("42") === DigitString("042")res0: Boolean = false
scala> DigitString("42").toInt === DigitString("042").toIntres1: Boolean = true
John C. Reynolds: Using category theory to design implicit conversions and generic operators.
Saturday, August 9, 2014
![Page 15: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/15.jpg)
How to decide:
1. To compile, an equality comparison must be interesting: values of types L and R can be either equal or unequal.
2. Allow select implicit conversions to be enabled, and recommend that non-widening conversions (non-injections) not be enabled.
Saturday, August 9, 2014
![Page 16: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/16.jpg)
scala> (Some(1): Option[Int]) === Some(1)res0: Boolean = true
scala> Some(1) === (Some(1): Option[Int])res1: Boolean = true
scala> Some(1) === Some(1)res2: Boolean = true
What about the implicit conversion from subtype to supertype (<:<)?
Saturday, August 9, 2014
![Page 17: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/17.jpg)
scala> def eqv[T](a: T, b: T): Boolean = a === beqv: [T](a: T, b: T)Boolean
scala> eqv(1, ())res3: Boolean = false
scala> ((i: Int) => i + 1) === ((i: Int) => i + 1)res4: Boolean = false
Even though <:< is an injection, is it always desireable?
Saturday, August 9, 2014
![Page 18: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/18.jpg)
EqualityPolicy
UncheckedEquality CheckedEquality EnabledEquality
Saturday, August 9, 2014
![Page 19: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/19.jpg)
scala> import EnabledEquality._import EnabledEquality._
scala> def eqv[T](a: T, b: T): Boolean = a === b<console>:19: error: types T and T do not adhere to the type constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.EqualityConstraint[T,T] def eqv[T](a: T, b: T): Boolean = a === b ^
scala> ((i: Int) => i + 1) === ((i: Int) => i + 1)<console>:20: error: types Int => Int and Int => Int do not adhere to the type constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.EqualityConstraint[Int => Int,Int => Int] ((i: Int) => i + 1) === ((i: Int) => i + 1) ^
EnabledEquality benefit
Saturday, August 9, 2014
![Page 20: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/20.jpg)
scala> case class Person(name: String)defined class Person
scala> Person("Sue") === Person("Sue")<console>:22: error: types Person and Person do not adhere to the type constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.EqualityConstraint[Person,Person] Person("Sue") === Person("Sue") ^
scala> implicit val enabler = new EqualityEnabledFor[Person]enabler: org.scalactic.EqualityEnabledFor[Person] = org.scalactic.EqualityEnabledFor@1289d391
scala> Person("Sue") === Person("Sue")res2: Boolean = true
EnabledEquality cost
Saturday, August 9, 2014
![Page 21: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/21.jpg)
scala> 1 should === ("one")<console>:23: error: types Int and String do not adhere to the type constraint selected for the === and !== operators; the missing implicit parameter is of type org.scalactic.Constraint[Int,String] 1 should === ("one") ^
scala> 1 should equal ("one")<console>:23: error: could not find implicit value for parameter typeClass1: org.scalactic.enablers.EvidenceThat[String]#CanEqual[Int] 1 should equal ("one") ^
scala> 1 should be ("one")<console>:23: error: could not find implicit value for parameter typeClass1: org.scalactic.enablers.EvidenceThat[String]#CanEqual[Int] 1 should be ("one") ^
scala> 1 should be_== ("one")org.scalatest.exceptions.TestFailedException: 1 was not equal to "one"
ScalaTest's equal, be, and be_==
Saturday, August 9, 2014
![Page 22: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/22.jpg)
scala> List(1, 2, 3) should contain ("one")<console>:23: error: could not find implicit value for parameter typeClass1: org.scalactic.enablers.EvidenceThat[String]#CanBeContainedIn[List[Int]] List(1, 2, 3) should contain ("one") ^
scala> List(1, 2, 3) should contain oneOf ("one", "two")<console>:23: error: could not find implicit value for parameter evidence: org.scalactic.enablers.EvidenceThat[String]#CanBeContainedIn[List[Int]] List(1, 2, 3) should contain oneOf("one", "two") ^
scala> 1 isIn List(1, 2, 3)res14: Boolean = true
scala> "one" isIn List(1, 2, 3)<console>:23: error: Could not find evidence that String can be contained in List[Int]; the missing implicit parameter is of type org.scalactic.enablers.ContainingConstraint[List[Int],String] "one" isIn List(1, 2, 3) ^
ScalaTest's contain, Scalactic's isIn/isNotIn
Saturday, August 9, 2014
![Page 23: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/23.jpg)
if sufficientTimeRemains then (Q => A) else thanks
artima.com/shop
15% discountcoupon code:
BYTHEBAY2014
Saturday, August 9, 2014
![Page 24: Equality For All!](https://reader031.vdocuments.net/reader031/viewer/2022020122/549f89a8ac79594c768b4a9a/html5/thumbnails/24.jpg)
scala> 1L === 1 res0: Boolean = true
scala> 1 === 1L<console>:14: error: could not find implicit value for parameter F0: scalaz.Equal[Any] 1 === 1L ^
Scalaz or Spire
Scalacticscala> 1L === 1res7: Boolean = true
scala> 1 === 1Lres8: Boolean = true
Equal[T], Eq[T]
EqualityConstraint[L, R]
Equivalence[T]
Saturday, August 9, 2014