Re: Make Eq type class single method

Oleg Grenrus <[email protected]> Wed, 20 Oct 2021 19:49:52 +0300
Newsgroups gmane.comp.lang.haskell.libraries
Message-ID <[email protected]>
The changes needed to compile GHC itself are small-ish, and indeed include
the primitive types and few others.

text library needs change as well, which is unfortunate

 import Prelude (Char, Bool(..), Int, Maybe(..), String,
-                Eq(..), Ord(..), Ordering(..), (++),
+                Eq(..), (/=), Ord(..), Ordering(..), (++),
                 Read(..),

It's somewhat common to import things explicitly from Prelude,
I do this often when writing something with wide base support,
and which uses the same names as in Prelude.
But this is simple change to do downstream.

I attach the patches for ghc, containers and text.
I encourage someone (Joachim?) to run the nofib suite.

Also, we coudl add builtin rewrite rules, rewriting not (eqInt8 x y)
to neInt8 x y if some benchmarks show that it would be beneficial.

- Oleg

On 20.10.2021 18.15, Viktor Dukhovni wrote:
> On Wed, Oct 20, 2021 at 04:39:21PM +0200, Joachim Breitner wrote:
>
>> I am revisiting some educational material about Haskell, and I stumble
>> over something that I keep stumbling over. I thought there was prior
>> discussion, but I couldn’t find it (operators hard hard to google for).
>>
>> Why does Eq have a (/=) method?
> For primitive types CPUs often have both '==' and '/=' instructions,
> and so a direct call to `(/=)` may be more efficient than calling
> `(not .) . (==)`.  The base package defines:
>
>     instance Eq Int8 where
>         (==) = eqInt8
>         (/=) = neInt8
>     instance Eq Int16 where
>         (==) = eqInt16
>         (/=) = neInt16
>     instance Eq Int32 where
>         (==) = eqInt32
>         (/=) = neInt32
>     instance Eq Int64 where
>         (==) = eqInt64
>         (/=) = neInt64
>
>     instance Eq Word8 where
>         (==) = eqWord8
>         (/=) = neWord8
>     instance Eq Word16 where
>         (==) = eqWord16
>         (/=) = neWord16
>     instance Eq Word32 where
>         (==) = eqWord32
>         (/=) = neWord32
>     instance Eq Word64 where
>         (==) = eqWord64
>         (/=) = neWord64
>
> There are also various cases involving equality/inequaility on
> getUnique, ...
>
>     compiler/GHC/Core/Coercion/Axiom.hs:instance Eq (CoAxiom br) where
>     compiler/GHC/Core/Coercion/Axiom.hs-    a == b = getUnique a == getUnique b
>     compiler/GHC/Core/Coercion/Axiom.hs-    a /= b = getUnique a /= getUnique b
>
>     compiler/GHC/Core/Class.hs:instance Eq Class where
>     compiler/GHC/Core/Class.hs-    c1 == c2 = classKey c1 == classKey c2
>     compiler/GHC/Core/Class.hs-    c1 /= c2 = classKey c1 /= classKey c2
>
> I don't know whether optimisations to use direct CPU instructions pay
> their way relative to the cost of larger Eq dictionaries in other
> contexts, but this seems to be at least a plausible reason.
>

_______________________________________________
Libraries mailing list
[email protected]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
containers.diff (text/x-patch, 1 KB)
diff --git a/containers/src/Data/IntMap/Internal.hs b/containers/src/Data/IntMap/Internal.hs
index d680cc2..7ba8be0 100644
--- a/containers/src/Data/IntMap/Internal.hs
+++ b/containers/src/Data/IntMap/Internal.hs
@@ -3272,7 +3272,6 @@ data Distinct = Distinct | Nondistinct
 --------------------------------------------------------------------}
 instance Eq a => Eq (IntMap a) where
   t1 == t2  = equal t1 t2
-  t1 /= t2  = nequal t1 t2
 
 equal :: Eq a => IntMap a -> IntMap a -> Bool
 equal (Bin p1 m1 l1 r1) (Bin p2 m2 l2 r2)
diff --git a/containers/src/Data/IntSet/Internal.hs b/containers/src/Data/IntSet/Internal.hs
index cb8a161..91e7b70 100644
--- a/containers/src/Data/IntSet/Internal.hs
+++ b/containers/src/Data/IntSet/Internal.hs
@@ -1193,7 +1193,6 @@ data Inserted = Inserted !IntSet ![Key]
 --------------------------------------------------------------------}
 instance Eq IntSet where
   t1 == t2  = equal t1 t2
-  t1 /= t2  = nequal t1 t2
 
 equal :: IntSet -> IntSet -> Bool
 equal (Bin p1 m1 l1 r1) (Bin p2 m2 l2 r2)
text.diff (text/x-patch, 483 B)
diff --git a/src/Data/Text.hs b/src/Data/Text.hs
index 0734cba..2de8779 100644
--- a/src/Data/Text.hs
+++ b/src/Data/Text.hs
@@ -208,7 +208,7 @@ module Data.Text
     ) where
 
 import Prelude (Char, Bool(..), Int, Maybe(..), String,
-                Eq(..), Ord(..), Ordering(..), (++),
+                Eq(..), (/=), Ord(..), Ordering(..), (++),
                 Read(..),
                 (&&), (||), (+), (-), (.), ($), ($!), (>>),
                 not, return, otherwise, quot)
ghc.diff (text/x-patch, 10.5 KB)
diff --git a/compiler/GHC/Core/Class.hs b/compiler/GHC/Core/Class.hs
index 99359fc2a1..060c77907a 100644
--- a/compiler/GHC/Core/Class.hs
+++ b/compiler/GHC/Core/Class.hs
@@ -340,7 +340,6 @@ We compare @Classes@ by their keys (which include @Uniques@).
 
 instance Eq Class where
     c1 == c2 = classKey c1 == classKey c2
-    c1 /= c2 = classKey c1 /= classKey c2
 
 instance Uniquable Class where
     getUnique c = classKey c
diff --git a/compiler/GHC/Core/Coercion/Axiom.hs b/compiler/GHC/Core/Coercion/Axiom.hs
index 5db9f17161..12564ea2e4 100644
--- a/compiler/GHC/Core/Coercion/Axiom.hs
+++ b/compiler/GHC/Core/Coercion/Axiom.hs
@@ -459,7 +459,6 @@ See also:
 
 instance Eq (CoAxiom br) where
     a == b = getUnique a == getUnique b
-    a /= b = getUnique a /= getUnique b
 
 instance Uniquable (CoAxiom br) where
     getUnique = co_ax_unique
diff --git a/compiler/GHC/Core/DataCon.hs b/compiler/GHC/Core/DataCon.hs
index c4c7f90a71..f6888cb4d3 100644
--- a/compiler/GHC/Core/DataCon.hs
+++ b/compiler/GHC/Core/DataCon.hs
@@ -869,7 +869,6 @@ for the details of this transformation.
 
 instance Eq DataCon where
     a == b = getUnique a == getUnique b
-    a /= b = getUnique a /= getUnique b
 
 instance Uniquable DataCon where
     getUnique = dcUnique
diff --git a/compiler/GHC/Core/PatSyn.hs b/compiler/GHC/Core/PatSyn.hs
index c3aeb87c27..5e929e16e0 100644
--- a/compiler/GHC/Core/PatSyn.hs
+++ b/compiler/GHC/Core/PatSyn.hs
@@ -344,7 +344,6 @@ We cannot in general represent this by a value of type Type:
 
 instance Eq PatSyn where
     (==) = (==) `on` getUnique
-    (/=) = (/=) `on` getUnique
 
 instance Uniquable PatSyn where
     getUnique = psUnique
diff --git a/compiler/GHC/Core/TyCon.hs b/compiler/GHC/Core/TyCon.hs
index 8a4da0f541..52c2122786 100644
--- a/compiler/GHC/Core/TyCon.hs
+++ b/compiler/GHC/Core/TyCon.hs
@@ -2822,7 +2822,6 @@ mkTyConTagMap tycon =
 
 instance Eq TyCon where
     a == b = getUnique a == getUnique b
-    a /= b = getUnique a /= getUnique b
 
 instance Uniquable TyCon where
     getUnique tc = tyConUnique tc
diff --git a/compiler/GHC/Types/Name.hs b/compiler/GHC/Types/Name.hs
index c2b76b1bfd..36c8e8aaf1 100644
--- a/compiler/GHC/Types/Name.hs
+++ b/compiler/GHC/Types/Name.hs
@@ -542,7 +542,6 @@ stableNameCmp (Name { n_sort = s1, n_occ = occ1 })
 -- | The same comments as for `Name`'s `Ord` instance apply.
 instance Eq Name where
     a == b = case (a `compare` b) of { EQ -> True;  _ -> False }
-    a /= b = case (a `compare` b) of { EQ -> False; _ -> True }
 
 -- | __Caution__: This instance is implemented via `nonDetCmpUnique`, which
 -- means that the ordering is not stable across deserialization or rebuilds.
diff --git a/compiler/GHC/Types/Unique.hs b/compiler/GHC/Types/Unique.hs
index 25075f47a1..680687d9f2 100644
--- a/compiler/GHC/Types/Unique.hs
+++ b/compiler/GHC/Types/Unique.hs
@@ -263,7 +263,6 @@ The alternatives are:
 
 instance Eq Unique where
     a == b = eqUnique a b
-    a /= b = not (eqUnique a b)
 
 instance Uniquable Unique where
     getUnique u = u
diff --git a/libraries/base/GHC/Int.hs b/libraries/base/GHC/Int.hs
index 4e10e0ca41..0fcb3a02fe 100644
--- a/libraries/base/GHC/Int.hs
+++ b/libraries/base/GHC/Int.hs
@@ -70,7 +70,6 @@ data {-# CTYPE "HsInt8" #-} Int8 = I8# Int8#
 -- | @since 2.01
 instance Eq Int8 where
     (==) = eqInt8
-    (/=) = neInt8
 
 eqInt8, neInt8 :: Int8 -> Int8 -> Bool
 eqInt8 (I8# x) (I8# y) = isTrue# (x `eqInt8#` y)
@@ -277,7 +276,6 @@ data {-# CTYPE "HsInt16" #-} Int16 = I16# Int16#
 -- | @since 2.01
 instance Eq Int16 where
     (==) = eqInt16
-    (/=) = neInt16
 
 eqInt16, neInt16 :: Int16 -> Int16 -> Bool
 eqInt16 (I16# x) (I16# y) = isTrue# (x `eqInt16#` y)
@@ -481,7 +479,6 @@ data {-# CTYPE "HsInt32" #-} Int32 = I32# Int32#
 -- | @since 2.01
 instance Eq Int32 where
     (==) = eqInt32
-    (/=) = neInt32
 
 eqInt32, neInt32 :: Int32 -> Int32 -> Bool
 eqInt32 (I32# x) (I32# y) = isTrue# (x `eqInt32#` y)
@@ -889,7 +886,6 @@ data {-# CTYPE "HsInt64" #-} Int64 = I64# Int#
 -- | @since 2.01
 instance Eq Int64 where
     (==) = eqInt64
-    (/=) = neInt64
 
 eqInt64, neInt64 :: Int64 -> Int64 -> Bool
 eqInt64 (I64# x) (I64# y) = isTrue# (x ==# y)
diff --git a/libraries/base/GHC/Word.hs b/libraries/base/GHC/Word.hs
index dd803c55b4..90a3490604 100644
--- a/libraries/base/GHC/Word.hs
+++ b/libraries/base/GHC/Word.hs
@@ -79,7 +79,6 @@ data {-# CTYPE "HsWord8" #-} Word8
 -- | @since 2.01
 instance Eq Word8 where
     (==) = eqWord8
-    (/=) = neWord8
 
 eqWord8, neWord8 :: Word8 -> Word8 -> Bool
 eqWord8 (W8# x) (W8# y) = isTrue# (x `eqWord8#` y)
@@ -265,7 +264,6 @@ data {-# CTYPE "HsWord16" #-} Word16 = W16# Word16#
 -- | @since 2.01
 instance Eq Word16 where
     (==) = eqWord16
-    (/=) = neWord16
 
 eqWord16, neWord16 :: Word16 -> Word16 -> Bool
 eqWord16 (W16# x) (W16# y) = isTrue# (x `eqWord16#` y)
@@ -493,7 +491,6 @@ data {-# CTYPE "HsWord32" #-} Word32 = W32# Word32#
 -- | @since 2.01
 instance Eq Word32 where
     (==) = eqWord32
-    (/=) = neWord32
 
 eqWord32, neWord32 :: Word32 -> Word32 -> Bool
 eqWord32 (W32# x) (W32# y) = isTrue# (x `eqWord32#` y)
@@ -673,7 +670,6 @@ data {-# CTYPE "HsWord64" #-} Word64 = W64# Word64#
 -- | @since 2.01
 instance Eq Word64 where
     (==) = eqWord64
-    (/=) = neWord64
 
 eqWord64, neWord64 :: Word64 -> Word64 -> Bool
 eqWord64 (W64# x) (W64# y) = isTrue# (x `eqWord64#` y)
@@ -816,7 +812,6 @@ data {-# CTYPE "HsWord64" #-} Word64 = W64# Word#
 -- | @since 2.01
 instance Eq Word64 where
     (==) = eqWord64
-    (/=) = neWord64
 
 eqWord64, neWord64 :: Word64 -> Word64 -> Bool
 eqWord64 (W64# x) (W64# y) = isTrue# (x `eqWord#` y)
diff --git a/libraries/base/Prelude.hs b/libraries/base/Prelude.hs
index 66ea603e72..9010747e89 100644
--- a/libraries/base/Prelude.hs
+++ b/libraries/base/Prelude.hs
@@ -38,7 +38,7 @@ module Prelude (
     fst, snd, curry, uncurry,
 
     -- ** Basic type classes
-    Eq((==), (/=)),
+    Eq((==)), (/=),
     Ord(compare, (<), (<=), (>=), (>), max, min),
     Enum(succ, pred, toEnum, fromEnum, enumFrom, enumFromThen,
          enumFromTo, enumFromThenTo),
diff --git a/libraries/containers b/libraries/containers
--- a/libraries/containers
+++ b/libraries/containers
@@ -1 +1 @@
-Subproject commit f90e38cb170dcd68de8660dfd9d0e879921acc28
+Subproject commit f90e38cb170dcd68de8660dfd9d0e879921acc28-dirty
diff --git a/libraries/ghc-bignum/src/GHC/Num/BigNat.hs b/libraries/ghc-bignum/src/GHC/Num/BigNat.hs
index b6afc533fb..3b9a054e62 100644
--- a/libraries/ghc-bignum/src/GHC/Num/BigNat.hs
+++ b/libraries/ghc-bignum/src/GHC/Num/BigNat.hs
@@ -1609,7 +1609,6 @@ bigNatFromWordArray wa n = BN# (bigNatFromWordArray# wa n)
 
 instance Eq BigNat where
    BN# a == BN# b = bigNatEq a b
-   BN# a /= BN# b = bigNatNe a b
 
 instance Ord BigNat where
    (BN# a) `compare` (BN# b) = bigNatCompare a b
diff --git a/libraries/ghc-bignum/src/GHC/Num/Integer.hs b/libraries/ghc-bignum/src/GHC/Num/Integer.hs
index f0cfcb81b0..f45fb16ff7 100644
--- a/libraries/ghc-bignum/src/GHC/Num/Integer.hs
+++ b/libraries/ghc-bignum/src/GHC/Num/Integer.hs
@@ -326,7 +326,6 @@ integerGe# _ _                             = 1#
 
 instance Eq Integer where
    (==) = integerEq
-   (/=) = integerNe
 
 -- | Compare two Integer
 integerCompare :: Integer -> Integer -> Ordering
diff --git a/libraries/ghc-bignum/src/GHC/Num/Natural.hs b/libraries/ghc-bignum/src/GHC/Num/Natural.hs
index 72b646501d..60fb4dedef 100644
--- a/libraries/ghc-bignum/src/GHC/Num/Natural.hs
+++ b/libraries/ghc-bignum/src/GHC/Num/Natural.hs
@@ -28,7 +28,6 @@ data Natural
 
 instance Eq Natural where
    (==) = naturalEq
-   (/=) = naturalNe
 
 instance Ord Natural where
    compare = naturalCompare
diff --git a/libraries/ghc-prim/GHC/Classes.hs b/libraries/ghc-prim/GHC/Classes.hs
index 2c874fb241..b0d674c49f 100644
--- a/libraries/ghc-prim/GHC/Classes.hs
+++ b/libraries/ghc-prim/GHC/Classes.hs
@@ -35,7 +35,7 @@ module GHC.Classes(
     IP(..),
 
     -- * Equality and ordering
-    Eq(..),
+    Eq(..), (/=),
     Ord(..),
     -- ** Monomorphic equality operators
     -- $matching_overloaded_methods_in_rules
@@ -122,11 +122,13 @@ Currently this is only done for @('==')@, @('/=')@, @('<')@, @('<=')@, @('>')@,
 and @('>=')@ for the types in "GHC.Word" and "GHC.Int".
 -}
 
--- | The 'Eq' class defines equality ('==') and inequality ('/=').
+-- | The 'Eq' class defines equality ('==').
 -- All the basic datatypes exported by the "Prelude" are instances of 'Eq',
 -- and 'Eq' may be derived for any datatype whose constituents are also
 -- instances of 'Eq'.
 --
+-- The inequality ('/=') is moved out of the class in TODO.
+--
 -- The Haskell Report defines no laws for 'Eq'. However, instances are
 -- encouraged to follow these properties:
 --
@@ -135,18 +137,13 @@ and @('>=')@ for the types in "GHC.Word" and "GHC.Int".
 -- [__Transitivity__]: if @x == y && y == z@ = 'True', then @x == z@ = 'True'
 -- [__Extensionality__]: if @x == y@ = 'True' and @f@ is a function
 -- whose return type is an instance of 'Eq', then @f x == f y@ = 'True'
--- [__Negation__]: @x /= y@ = @not (x == y)@
---
--- Minimal complete definition: either '==' or '/='.
 --
 class  Eq a  where
-    (==), (/=)           :: a -> a -> Bool
+    (==)           :: a -> a -> Bool
 
-    {-# INLINE (/=) #-}
-    {-# INLINE (==) #-}
-    x /= y               = not (x == y)
-    x == y               = not (x /= y)
-    {-# MINIMAL (==) | (/=) #-}
+(/=) :: Eq a => a -> a -> Bool
+x /= y               = not (x == y)
+{-# INLINE (/=) #-}
 
 deriving instance Eq ()
 deriving instance Eq a => Eq (Solo a)
@@ -205,7 +202,6 @@ deriving instance Eq Ordering
 
 instance Eq Word where
     (==) = eqWord
-    (/=) = neWord
 
 -- See GHC.Classes#matching_overloaded_methods_in_rules
 {-# INLINE [1] eqWord #-}
@@ -217,7 +213,6 @@ eqWord, neWord :: Word -> Word -> Bool
 -- See GHC.Classes#matching_overloaded_methods_in_rules
 instance Eq Char where
     (==) = eqChar
-    (/=) = neChar
 
 -- See GHC.Classes#matching_overloaded_methods_in_rules
 {-# INLINE [1] eqChar #-}
@@ -268,7 +263,6 @@ eqDouble :: Double -> Double -> Bool
 
 instance Eq Int where
     (==) = eqInt
-    (/=) = neInt
 
 -- See GHC.Classes#matching_overloaded_methods_in_rules
 {-# INLINE [1] eqInt #-}
diff --git a/libraries/text b/libraries/text
--- a/libraries/text
+++ b/libraries/text
@@ -1 +1 @@
-Subproject commit 66bb23baf6ba7803c1a87cfeec16671b26acde00
+Subproject commit 66bb23baf6ba7803c1a87cfeec16671b26acde00-dirty
diff --git a/libraries/unix b/libraries/unix
--- a/libraries/unix
+++ b/libraries/unix
@@ -1 +1 @@
-Subproject commit 21437f20a41eb1a4c7d42fc402fe91350eb8b03d
+Subproject commit 21437f20a41eb1a4c7d42fc402fe91350eb8b03d-dirty