Make Eq type class single method

Hi list, 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? Semantically, I don’t see a reason why an instance might want to behave differently than the default method. And (in contrast to, say, Ord), I would be surprised if there are noticable performance benefits to be gained from implementing (/=) separately. On the other hand, this is likely the first time a learner will encounter a class with default methods, and it’s awkward to explain “this mechanism is useful if you can have an optimized implementation, but, eh, here it isn’t really possible”. If we’d design the language now, would we include (/=) as a method? If no: would it be worth removing it? Yes, every change is annoying, but if are going to keep using Haskell the next 30 days, it may pay off? And it might not be too bad: Remove it from base, but teach GHC to not error out if an instance defines (/=), but print a warning and otherwise ignore it. Libraries can remove it if it is defined, which is a backwards-compatible change. WDYT? Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

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?
Perhaps sometimes it is easier to define (/=) and use the default definition of (==) in terms of it?

I'm finding it hard to think of a case where (/=) would be any easier to define than (==).
On Wed, Oct 20, 2021 at 10:43 AM Tom Ellis < tom-lists-haskell-cafe-2017@jaguarpaw.co.uk> 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?
Perhaps sometimes it is easier to define (/=) and use the default definition of (==) in terms of it? _______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
-- brandon s allbery kf8nh allbery.b@gmail.com

As in so many other cases, documenting the design decisions (that lead to (/=) in Eq) would have helped [even for the initial reflection on this design]. I see that in another thread there are reasons brought forward in favor of (/=). So if the outcome of this discussion is "no change", the reasons should be added to the documentation of the Eq class. --Andreas On 2021-10-20 16:48, Brandon Allbery wrote:
I'm finding it hard to think of a case where (/=) would be any easier to define than (==).
On Wed, Oct 20, 2021 at 10:43 AM Tom Ellis
mailto:tom-lists-haskell-cafe-2017@jaguarpaw.co.uk> 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?
Perhaps sometimes it is easier to define (/=) and use the default definition of (==) in terms of it?

Hi, oh, and I completely forgot to add: Am Mittwoch, dem 20.10.2021 um 16:39 +0200 schrieb Joachim Breitner:
If no: would it be worth removing it?
Yes, every change is annoying, but if are going to keep using Haskell the next 30 days, it may pay off? And it might not be too bad: Remove it from base, but teach GHC to not error out if an instance defines (/=), but print a warning and otherwise ignore it. Libraries can remove it if it is defined, which is a backwards-compatible change.
And an (important?) benefit would be that now Eq would then be a single-method class, which are compiled by GHC more efficiently – they essentially _are_ the (==) function, not a tuple of both methods. For something as low-level as (==), this might be measurable… Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

Hi,
Sometimes, /= is semidecidable and == only cosemidecidable. E.g. Exact Real
Arithmetic.
Best,
Jens
On Wed, 20 Oct 2021 at 15:56, Joachim Breitner
Hi,
oh, and I completely forgot to add:
Am Mittwoch, dem 20.10.2021 um 16:39 +0200 schrieb Joachim Breitner:
If no: would it be worth removing it?
Yes, every change is annoying, but if are going to keep using Haskell the next 30 days, it may pay off? And it might not be too bad: Remove it from base, but teach GHC to not error out if an instance defines (/=), but print a warning and otherwise ignore it. Libraries can remove it if it is defined, which is a backwards-compatible change.
And an (important?) benefit would be that now Eq would then be a single-method class, which are compiled by GHC more efficiently – they essentially _are_ the (==) function, not a tuple of both methods. For something as low-level as (==), this might be measurable…
Cheers, Joachim
-- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

Hi, Am Mittwoch, dem 20.10.2021 um 16:05 +0100 schrieb Jens Blanck:
Sometimes, /= is semidecidable and == only cosemidecidable. E.g. Exact Real Arithmetic.
does that reasoning apply here? (/=) still returns True or False, so they are still both deciding the same question. Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

On Wed, Oct 20, 2021 at 04:55:15PM +0200, Joachim Breitner wrote:
Am Mittwoch, dem 20.10.2021 um 16:39 +0200 schrieb Joachim Breitner:
If no: would it be worth removing it?
Yes, every change is annoying, but if are going to keep using Haskell the next 30 days, it may pay off? And it might not be too bad: Remove it from base, but teach GHC to not error out if an instance defines (/=), but print a warning and otherwise ignore it. Libraries can remove it if it is defined, which is a backwards-compatible change.
And an (important?) benefit would be that now Eq would then be a single-method class, which are compiled by GHC more efficiently – they essentially _are_ the (==) function, not a tuple of both methods. For something as low-level as (==), this might be measurable…
Since (/=) is an Eq class method it can be implemented on (Int, Int) as (x1, x2) /= (y1, y2) = (x1 /= y1) || (x2 /= y2) If it were not a class method it would have to be implemented as, after inlining (x1, x2) /= (y1, y2) = not ((x1 == y1) && (x2 == y2)) Might not the latter be less efficient, because of the extra `not`? Tom

Hi, Am Mittwoch, dem 20.10.2021 um 16:08 +0100 schrieb Tom Ellis:
Since (/=) is an Eq class method it can be implemented on (Int, Int) as
(x1, x2) /= (y1, y2) = (x1 /= y1) || (x2 /= y2)
If it were not a class method it would have to be implemented as, after inlining
(x1, x2) /= (y1, y2) = not ((x1 == y1) && (x2 == y2))
Might not the latter be less efficient, because of the extra `not`?
maybe. I think I’ll simply try it out, and see if for example GHC itself speeds up if (/=) is not a class method. Note that if the inner Eq is using the default implementation, then there will be _two_ not there. I’d expect that not will quickly inline, and the cost of an inlined not might be small compared to the cost of projecting (/=) out of the class dictionary (in polymorphic code). And in other cases, the (/=) might inline and the not might be fused with the surrounding code via case- of-case. Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

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. -- Viktor.

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.

Am Mi., 20. Okt. 2021 um 18:51 Uhr schrieb Oleg Grenrus : [...] 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. Hopefully such peephole optimizations are done much more generally further
down the compilation pipeline, otherwise we will have more serious
performance problems than the removal of (/=) from Eq. ;-)

It might be they are not required. In example like {-# OPTIONS_GHC -ddump-simpl -dsuppress-all #-} import System.Environment import Data.Word main :: IO () main = do [x',y'] <- getArgs let x, y :: Word x = read x' y = read y' print (not (x == y)) GHC is smart enough to just swap the branches, relevant optimized core looks like case x_a4Tc of { W# x2_a3Wb -> case x1_X4Ua of { W# y_a3Wf -> case eqWord# x2_a3Wb y_a3Wf of { __DEFAULT -> $fShowBool2; 1# -> $fShowBool4 } } }; where `$fShowBool*` are (probably, I haven't checked) printing True or False. I.e. expressions which are "case (not x) of True -> ..., False -> ..." optimize well, early enough. (IIRC that's the example used for case-of-case optimization). - Oleg On 20.10.2021 19.55, Sven Panne wrote:
Am Mi., 20. Okt. 2021 um 18:51 Uhr schrieb Oleg Grenrus
mailto:oleg.grenrus@iki.fi>: [...] 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.
Hopefully such peephole optimizations are done much more generally further down the compilation pipeline, otherwise we will have more serious performance problems than the removal of (/=) from Eq. ;-)

Hi, parallel work… should have linked to https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6793 earlier. But yes, it is rather simple to make that change. Am Mittwoch, dem 20.10.2021 um 19:49 +0300 schrieb Oleg Grenrus:
I attach the patches for ghc, containers and text. I encourage someone (Joachim?) to run the nofib suite.
nofib might be too specialized; I think “ghc compiling Cabal” is a good benchmark. I’ll do that eventually, if GHC’s CI doesn’t do that already for me (I’ll ask Ben what kind of infrastructure we have for that – doing it manually and correctly is tedious and error-prone), maybe tomorrow. Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

So one thing I’d like to highlight, that I don’t know if we could bake into
ghc safely and might not be relevant here is:
At least in some cases, implementations of EQ may use
“reallyUnsafePtrEquality#”, as a never false positive check that two heap
values are the same heap object. Naively this seems like it’s probably
orthogonal to the matter of Eq being a single or double method type class,
but I figure it’s worth asking out loud
On Wed, Oct 20, 2021 at 4:29 PM Joachim Breitner
Hi,
parallel work… should have linked to https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6793 earlier. But yes, it is rather simple to make that change.
Am Mittwoch, dem 20.10.2021 um 19:49 +0300 schrieb Oleg Grenrus:
I attach the patches for ghc, containers and text. I encourage someone (Joachim?) to run the nofib suite.
nofib might be too specialized; I think “ghc compiling Cabal” is a good benchmark. I’ll do that eventually, if GHC’s CI doesn’t do that already for me (I’ll ask Ben what kind of infrastructure we have for that – doing it manually and correctly is tedious and error-prone), maybe tomorrow.
Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

Not relevant. One question i have though: in the case of primitive types,
can switching from a primitive `/=` to one based on `==` change the order
of branches and thereby affect performance for better or worse?
On Thu, Oct 21, 2021, 10:19 AM Carter Schonwald
So one thing I’d like to highlight, that I don’t know if we could bake into ghc safely and might not be relevant here is:
At least in some cases, implementations of EQ may use “reallyUnsafePtrEquality#”, as a never false positive check that two heap values are the same heap object. Naively this seems like it’s probably orthogonal to the matter of Eq being a single or double method type class, but I figure it’s worth asking out loud
On Wed, Oct 20, 2021 at 4:29 PM Joachim Breitner
wrote: Hi,
parallel work… should have linked to https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6793 earlier. But yes, it is rather simple to make that change.
Am Mittwoch, dem 20.10.2021 um 19:49 +0300 schrieb Oleg Grenrus:
I attach the patches for ghc, containers and text. I encourage someone (Joachim?) to run the nofib suite.
nofib might be too specialized; I think “ghc compiling Cabal” is a good benchmark. I’ll do that eventually, if GHC’s CI doesn’t do that already for me (I’ll ask Ben what kind of infrastructure we have for that – doing it manually and correctly is tedious and error-prone), maybe tomorrow.
Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

Wasn't it already shown that ghc already swaps the order of branches to
match?
On Thu, Oct 21, 2021 at 11:13 AM David Feuer
Not relevant. One question i have though: in the case of primitive types, can switching from a primitive `/=` to one based on `==` change the order of branches and thereby affect performance for better or worse?
On Thu, Oct 21, 2021, 10:19 AM Carter Schonwald < carter.schonwald@gmail.com> wrote:
So one thing I’d like to highlight, that I don’t know if we could bake into ghc safely and might not be relevant here is:
At least in some cases, implementations of EQ may use “reallyUnsafePtrEquality#”, as a never false positive check that two heap values are the same heap object. Naively this seems like it’s probably orthogonal to the matter of Eq being a single or double method type class, but I figure it’s worth asking out loud
On Wed, Oct 20, 2021 at 4:29 PM Joachim Breitner < mail@joachim-breitner.de> wrote:
Hi,
parallel work… should have linked to https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6793 earlier. But yes, it is rather simple to make that change.
Am Mittwoch, dem 20.10.2021 um 19:49 +0300 schrieb Oleg Grenrus:
I attach the patches for ghc, containers and text. I encourage someone (Joachim?) to run the nofib suite.
nofib might be too specialized; I think “ghc compiling Cabal” is a good benchmark. I’ll do that eventually, if GHC’s CI doesn’t do that already for me (I’ll ask Ben what kind of infrastructure we have for that – doing it manually and correctly is tedious and error-prone), maybe tomorrow.
Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
-- brandon s allbery kf8nh allbery.b@gmail.com

Hi, Am Mittwoch, dem 20.10.2021 um 22:28 +0200 schrieb Joachim Breitner:
nofib might be too specialized; I think “ghc compiling Cabal” is a good benchmark. I’ll do that eventually, if GHC’s CI doesn’t do that already for me (I’ll ask Ben what kind of infrastructure we have for that – doing it manually and correctly is tedious and error-prone), maybe tomorrow.
Thanks for Mortiz Angerman for giving me access to a quiet machine. Here my crude setup to measure the time it takes GHC to compiler Cabal: $ for branch in ghc ghc-single-method-eq; do echo $branch; cd $branch; for i in 1 2 3 4 5; do /run/current-system/sw/bin/time _build/stage1/bin/ghc -hidir tmp -odir tmp -fforce-recomp +RTS -t -ttmp/ghc.log -RTS -ilibraries/Cabal/Cabal/src -XNoPolyKinds Distribution.Simple -O2 -v0; done; cd ..; done ghc 128.67user 2.03system 2:10.60elapsed 128.92user 2.00system 2:10.76elapsed 129.36user 2.07system 2:11.30elapsed 128.83user 2.02system 2:10.71elapsed 128.76user 2.13system 2:10.71elapsed ghc-single-method-eq 128.14user 2.04system 2:10.02elapsed 128.14user 2.02system 2:10.00elapsed 127.68user 2.12system 2:09.64elapsed 128.10user 2.05system 2:10.02elapsed 129.23user 1.95system 2:11.05elapsed I guess that means no significant improvement… (Such measurements always leave me wondering if I draw the right conclusions, and my setup is good. I crave for some expertly-setup measurement system where I don't have to worry about that, and I simply get a result.) Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

Hi, current status of this train of thought: * Most people here were sympathetic about the removal of (/=) from Eq. * It would break a bit of code, but with an easy and backward- compatible fix. * The GHC implementation is straight forward: https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6793 * There is _no_ measurable performance benefit in real-world-code. A few of GHC’s perf regression tests benefit slightly: T12234(optasm) ghc/alloc 57633544.0 55409592.0 -3.9% GOOD T18304(normal) ghc/alloc 87566616.0 83503296.0 -4.6% GOOD T783(normal) ghc/alloc 389240752.0 377482584.0 -3.0% GOOD T1969(normal) ghc/max 19869024.0 19071720.0 -4.0% T11545(normal) ghc/max 35580832.0 33828936.0 -4.9% * There is a problem with rules matching on class ops of single method classes: https://gitlab.haskell.org/ghc/ghc/-/issues/20535 I consider this a GHC bug that needs to be fixed anyways, so let’s assume for now it will be fixed one way or another before Eq becomes a single method class. With this, I’d like to formally bring this change before the CLC, and ask for its support for (or explicit rejection of) this change. Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

On Mon, Oct 25, 2021 at 11:04:50AM +0200, Joachim Breitner wrote:
current status of this train of thought:
* Most people here were sympathetic about the removal of (/=) from Eq.
* It would break a bit of code, but with an easy and backward- compatible fix.
* The GHC implementation is straight forward: https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6793
* There is _no_ measurable performance benefit in real-world-code. A few of GHC’s perf regression tests benefit slightly:
T12234(optasm) ghc/alloc 57633544.0 55409592.0 -3.9% GOOD T18304(normal) ghc/alloc 87566616.0 83503296.0 -4.6% GOOD T783(normal) ghc/alloc 389240752.0 377482584.0 -3.0% GOOD T1969(normal) ghc/max 19869024.0 19071720.0 -4.0% T11545(normal) ghc/max 35580832.0 33828936.0 -4.9%
* There is a problem with rules matching on class ops of single method classes: https://gitlab.haskell.org/ghc/ghc/-/issues/20535 I consider this a GHC bug that needs to be fixed anyways, so let’s assume for now it will be fixed one way or another before Eq becomes a single method class.
I'm a bit lost. If there's no performance benefit then what is the benefit? Your original email alluded to
it’s awkward to explain “this mechanism is useful if you can have an optimized implementation, but, eh, here it isn’t really possible”.
Is that really the whole motivation? It's awkward? If so then breaking every library maintainer who has ever added an explicit definition of (/=) seems like far too high a price to pay. Haskell library maintainers are already crying out about the burden of simply keeping up with breaking changes (I see it a lot on Twitter). The amount of churn here for little benefit seems overwhelming. Tom

Hi Joachim, Thank you for this summary of your findings. In echo with Tom Ellis' response, I too find the added benefits a tad too slim when compared to the changes that will have to be undertaken by package authors who have (/=) defined in their instances. Let's focus on tackling problems that have plagued us for way too long instead! :) Cheers, Hécate Le 25/10/2021 à 11:04, Joachim Breitner a écrit :
Hi,
current status of this train of thought:
* Most people here were sympathetic about the removal of (/=) from Eq.
* It would break a bit of code, but with an easy and backward- compatible fix.
* The GHC implementation is straight forward: https://gitlab.haskell.org/ghc/ghc/-/merge_requests/6793
* There is _no_ measurable performance benefit in real-world-code. A few of GHC’s perf regression tests benefit slightly:
T12234(optasm) ghc/alloc 57633544.0 55409592.0 -3.9% GOOD T18304(normal) ghc/alloc 87566616.0 83503296.0 -4.6% GOOD T783(normal) ghc/alloc 389240752.0 377482584.0 -3.0% GOOD T1969(normal) ghc/max 19869024.0 19071720.0 -4.0% T11545(normal) ghc/max 35580832.0 33828936.0 -4.9%
* There is a problem with rules matching on class ops of single method classes: https://gitlab.haskell.org/ghc/ghc/-/issues/20535 I consider this a GHC bug that needs to be fixed anyways, so let’s assume for now it will be fixed one way or another before Eq becomes a single method class.
With this, I’d like to formally bring this change before the CLC, and ask for its support for (or explicit rejection of) this change.
Cheers, Joachim
-- Hécate ✨ 🐦: @TechnoEmpress IRC: Hecate WWW: https://glitchbra.in RUN: BSD

Hi, ah, yes, let me summarize my main motivation (perf benefits were just a side-benefit I was hoping for): You can’t implement (/=) faster than (==) (up to, in the worst case, the cost of a single `not`, which often gets optimized away anyways). As such, having (/=) in Eq was a (small) mistake back then, and it’s worth fixing. There is one time cost of asking developers to _remove_ code. But code that was probably not worth writing in the first place! And I don’t blame them, the Eq class _invites_ writing that code. Then the benefits are twofold: * No more awkwards explanations about silly things in the likely first type class that developers care about. * Less code to read, maintain, compile in all the libraries that _do_ define (/=) right now. * Devs who instantiate Eq in the future will not be tricked into wondering if they need to implement (/=) and why. So even if “helps teaching beginners” doesn’t beat “having to bug maintainers”, then maybe the second point (“saving all develpers time and effort in the future”) does? Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

I'd like to also add that there is another benefit - correctness. * Eq is easier to get right/harder to get wrong. Currently if you hand roll Eq, you could potentially implement both == and /=, but get one or the other wrong. With only == as a method, as long as you get that right, you are guaranteed that /= is also right. On Mon, 25 Oct 2021, at 2:22 PM, Joachim Breitner wrote:
Hi,
ah, yes, let me summarize my main motivation (perf benefits were just a side-benefit I was hoping for):
You can’t implement (/=) faster than (==) (up to, in the worst case, the cost of a single `not`, which often gets optimized away anyways).
As such, having (/=) in Eq was a (small) mistake back then, and it’s worth fixing.
There is one time cost of asking developers to _remove_ code. But code that was probably not worth writing in the first place! And I don’t blame them, the Eq class _invites_ writing that code.
Then the benefits are twofold:
* No more awkwards explanations about silly things in the likely first type class that developers care about.
* Less code to read, maintain, compile in all the libraries that _do_ define (/=) right now.
* Devs who instantiate Eq in the future will not be tricked into wondering if they need to implement (/=) and why.
So even if “helps teaching beginners” doesn’t beat “having to bug maintainers”, then maybe the second point (“saving all develpers time and effort in the future”) does?
Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

i agree with Tom and Hecate,
more strongly:
https://github.com/ghc/ghc/blob/98aa29d3fe447cce3407e6864b015892244bb475/lib...
the current definition DOES NOT require defining both.
as it currently stands, you only need to define one of '==' *XOR* '/='
this actually suggests an interesting and DIFFERENT ecosystem improvement,
(ignoring IEEE non-signalling NANs),
namely we add support for XOR to minimal pragma syntax and issue a warning
perhaps something like adding *(XOR "reason String" clauseExpr1
clauseexpr2)* ?
in which case for the Eq the example useage would be something like
{-# MINIMAL ((== ) || (/=)) & (XOR "it is seldom correct to define both ==
and /= explicitly, please be careful!" (==) (/=)) #-}
this also seems like a "newish contributor" ish friendly improvement in our
minimal pragma facilities that could be useful for other examples?
On Mon, Oct 25, 2021 at 9:29 AM Oliver Charles
I'd like to also add that there is another benefit - correctness.
- Eq is easier to get right/harder to get wrong. Currently if you hand roll Eq, you could potentially implement both == and /=, but get one or the other wrong. With only == as a method, as long as you get that right, you are guaranteed that /= is also right.
On Mon, 25 Oct 2021, at 2:22 PM, Joachim Breitner wrote:
Hi,
ah, yes, let me summarize my main motivation (perf benefits were just a side-benefit I was hoping for):
You can’t implement (/=) faster than (==) (up to, in the worst case, the cost of a single `not`, which often gets optimized away anyways).
As such, having (/=) in Eq was a (small) mistake back then, and it’s worth fixing.
There is one time cost of asking developers to _remove_ code. But code that was probably not worth writing in the first place! And I don’t blame them, the Eq class _invites_ writing that code.
Then the benefits are twofold:
* No more awkwards explanations about silly things in the likely first type class that developers care about.
* Less code to read, maintain, compile in all the libraries that _do_ define (/=) right now.
* Devs who instantiate Eq in the future will not be tricked into wondering if they need to implement (/=) and why.
So even if “helps teaching beginners” doesn’t beat “having to bug maintainers”, then maybe the second point (“saving all develpers time and effort in the future”) does?
Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

Something I like about the current definition of Eq is that I can choose which method to implement. If (/=) is more convenient, I implement that one and leave the other as default, and viceversa. Am Mo., 25. Okt. 2021 um 16:11 Uhr schrieb Carter Schonwald < carter.schonwald@gmail.com>:
i agree with Tom and Hecate, more strongly: https://github.com/ghc/ghc/blob/98aa29d3fe447cce3407e6864b015892244bb475/lib...
the current definition DOES NOT require defining both.
as it currently stands, you only need to define one of '==' *XOR* '/='
this actually suggests an interesting and DIFFERENT ecosystem improvement, (ignoring IEEE non-signalling NANs), namely we add support for XOR to minimal pragma syntax and issue a warning
perhaps something like adding *(XOR "reason String" clauseExpr1 clauseexpr2)* ?
in which case for the Eq the example useage would be something like
{-# MINIMAL ((== ) || (/=)) & (XOR "it is seldom correct to define both == and /= explicitly, please be careful!" (==) (/=)) #-}
this also seems like a "newish contributor" ish friendly improvement in our minimal pragma facilities that could be useful for other examples?
On Mon, Oct 25, 2021 at 9:29 AM Oliver Charles
wrote: I'd like to also add that there is another benefit - correctness.
- Eq is easier to get right/harder to get wrong. Currently if you hand roll Eq, you could potentially implement both == and /=, but get one or the other wrong. With only == as a method, as long as you get that right, you are guaranteed that /= is also right.
On Mon, 25 Oct 2021, at 2:22 PM, Joachim Breitner wrote:
Hi,
ah, yes, let me summarize my main motivation (perf benefits were just a side-benefit I was hoping for):
You can’t implement (/=) faster than (==) (up to, in the worst case, the cost of a single `not`, which often gets optimized away anyways).
As such, having (/=) in Eq was a (small) mistake back then, and it’s worth fixing.
There is one time cost of asking developers to _remove_ code. But code that was probably not worth writing in the first place! And I don’t blame them, the Eq class _invites_ writing that code.
Then the benefits are twofold:
* No more awkwards explanations about silly things in the likely first type class that developers care about.
* Less code to read, maintain, compile in all the libraries that _do_ define (/=) right now.
* Devs who instantiate Eq in the future will not be tricked into wondering if they need to implement (/=) and why.
So even if “helps teaching beginners” doesn’t beat “having to bug maintainers”, then maybe the second point (“saving all develpers time and effort in the future”) does?
Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

On Mon, 25 Oct 2021, Carter Schonwald wrote:
i agree with Tom and Hecate,more strongly: https://github.com/ghc/ghc/blob/98aa29d3fe447cce3407e6864b015892244bb475/lib... the current definition DOES NOT require defining both.
as it currently stands, you only need to define one of '==' XOR '/='
this actually suggests an interesting and DIFFERENT ecosystem improvement, (ignoring IEEE non-signalling NANs), namely we add support for XOR to minimal pragma syntax and issue a warning
perhaps something like adding (XOR "reason String" clauseExpr1 clauseexpr2) ?
The problem of implementing two methods in a contradictory way arises for any OR in MINIMAL pragma, right?

not necessarily ... there could be contradictory sets of methods! :) like the minimal sets for Field type class, the xor would be for defining '/' in terms of reciprocal and times or vice versa (/ vs recip) and likewise (negate vs minus) etc etc On Wed, Oct 27, 2021 at 6:38 AM Henning Thielemann < lemming@henning-thielemann.de> wrote:
On Mon, 25 Oct 2021, Carter Schonwald wrote:
i agree with Tom and Hecate,more strongly:
https://github.com/ghc/ghc/blob/98aa29d3fe447cce3407e6864b015892244bb475/lib...
the current definition DOES NOT require defining both.
as it currently stands, you only need to define one of '==' XOR '/='
this actually suggests an interesting and DIFFERENT ecosystem improvement, (ignoring IEEE non-signalling NANs), namely we add support for XOR to minimal pragma syntax and issue a warning
perhaps something like adding (XOR "reason String" clauseExpr1 clauseexpr2) ?
The problem of implementing two methods in a contradictory way arises for any OR in MINIMAL pragma, right?

I think the only time they're not potentially contradictory is when you're
dealing with singletons, and that's a pretty special case.
On Wed, Oct 27, 2021, 11:15 AM Carter Schonwald
not necessarily ... there could be contradictory sets of methods! :)
like the minimal sets for Field type class, the xor would be for defining '/' in terms of reciprocal and times or vice versa (/ vs recip) and likewise (negate vs minus) etc etc
On Wed, Oct 27, 2021 at 6:38 AM Henning Thielemann < lemming@henning-thielemann.de> wrote:
On Mon, 25 Oct 2021, Carter Schonwald wrote:
i agree with Tom and Hecate,more strongly:
https://github.com/ghc/ghc/blob/98aa29d3fe447cce3407e6864b015892244bb475/lib...
the current definition DOES NOT require defining both.
as it currently stands, you only need to define one of '==' XOR '/='
this actually suggests an interesting and DIFFERENT ecosystem improvement, (ignoring IEEE non-signalling NANs), namely we add support for XOR to minimal pragma syntax and issue a warning
perhaps something like adding (XOR "reason String" clauseExpr1 clauseexpr2) ?
The problem of implementing two methods in a contradictory way arises for any OR in MINIMAL pragma, right?
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

On Wed, Oct 27, 2021 at 11:14:45AM -0400, Carter Schonwald wrote:
not necessarily ... there could be contradictory sets of methods! :)
like the minimal sets for Field type class, the xor would be for defining '/' in terms of reciprocal and times or vice versa (/ vs recip) and likewise (negate vs minus) etc etc
Logically redundant definitions aren't always redundant in practice if one considers performance, floating point accuracy or even sometimes divergence. For example, foldl on infinite snoc lists is not definable in terms of foldr which diverges, though admittedly I rather think that infinite snoc lists violate all reasonable expectations of a Foldable instance. So in many cases redundancy warnings would not be viable. The Eq situation is likely more the exception that the rule. The SQL NULL instances that are False for both "==" and "/=" look like they could be a mistake to me, but presumably they work out OK in practice, and I would expect that e.g. the relevant `Ord` instances do return `EQ` for `compare Null Null`... Less clear is whether the non-lawful Eq defintion is in some predicably useful way essential. -- Viktor.

For what it's worth, Haskell says that NaN is `GT` NaN. So maybe it would also claim than NULL is `GT` NULL.
(NaN is not `==` to NaN, and is not `<=` to NaN, so it must be GT.)
—
Sent from my phone with K-9 Mail.
On 27 October 2021 15:32:40 UTC, Viktor Dukhovni
On Wed, Oct 27, 2021 at 11:14:45AM -0400, Carter Schonwald wrote:
not necessarily ... there could be contradictory sets of methods! :)
like the minimal sets for Field type class, the xor would be for defining '/' in terms of reciprocal and times or vice versa (/ vs recip) and likewise (negate vs minus) etc etc
Logically redundant definitions aren't always redundant in practice if one considers performance, floating point accuracy or even sometimes divergence.
For example, foldl on infinite snoc lists is not definable in terms of foldr which diverges, though admittedly I rather think that infinite snoc lists violate all reasonable expectations of a Foldable instance.
So in many cases redundancy warnings would not be viable. The Eq situation is likely more the exception that the rule.
The SQL NULL instances that are False for both "==" and "/=" look like they could be a mistake to me, but presumably they work out OK in practice, and I would expect that e.g. the relevant `Ord` instances do return `EQ` for `compare Null Null`... Less clear is whether the non-lawful Eq defintion is in some predicably useful way essential.
-- Viktor. _______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

NULL is not like NaN. It's perfectly sensible to stick `NULL` in a `Set` or
something.
On Wed, Oct 27, 2021, 8:09 PM Keith
For what it's worth, Haskell says that NaN is `GT` NaN. So maybe it would also claim than NULL is `GT` NULL.
(NaN is not `==` to NaN, and is not `<=` to NaN, so it must be GT.) — Sent from my phone with K-9 Mail.
On 27 October 2021 15:32:40 UTC, Viktor Dukhovni
wrote: On Wed, Oct 27, 2021 at 11:14:45AM -0400, Carter Schonwald wrote:
not necessarily ... there could be contradictory sets of methods! :)
like the minimal sets for Field type class, the xor would be for defining '/' in terms of reciprocal and times or vice versa (/ vs recip) and likewise (negate vs minus) etc etc
Logically redundant definitions aren't always redundant in practice if one considers performance, floating point accuracy or even sometimes divergence.
For example, foldl on infinite snoc lists is not definable in terms of foldr which diverges, though admittedly I rather think that infinite snoc lists violate all reasonable expectations of a Foldable instance.
So in many cases redundancy warnings would not be viable. The Eq situation is likely more the exception that the rule.
The SQL NULL instances that are False for both "==" and "/=" look like they could be a mistake to me, but presumably they work out OK in practice, and I would expect that e.g. the relevant `Ord` instances do return `EQ` for `compare Null Null`... Less clear is whether the non-lawful Eq defintion is in some predicably useful way essential.
-- Viktor. ------------------------------ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________
Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

Sure, you can consider NULL to be the top or bottom of the lattice. But in practice what is the point of putting it in a `Set`?
You extract all the phone numbers from a DB and stick 'em in a `Set`, and every missing number is mapped to the single NULL in the `Set`? And now you know from the `Set` that at least one number was missing?
—
Sent from my phone with K-9 Mail.
On 28 October 2021 00:14:11 UTC, David Feuer
NULL is not like NaN. It's perfectly sensible to stick `NULL` in a `Set` or something.
On Wed, Oct 27, 2021, 8:09 PM Keith
wrote: For what it's worth, Haskell says that NaN is `GT` NaN. So maybe it would also claim than NULL is `GT` NULL.
(NaN is not `==` to NaN, and is not `<=` to NaN, so it must be GT.) — Sent from my phone with K-9 Mail.
On 27 October 2021 15:32:40 UTC, Viktor Dukhovni
wrote: On Wed, Oct 27, 2021 at 11:14:45AM -0400, Carter Schonwald wrote:
not necessarily ... there could be contradictory sets of methods! :)
like the minimal sets for Field type class, the xor would be for defining '/' in terms of reciprocal and times or vice versa (/ vs recip) and likewise (negate vs minus) etc etc
Logically redundant definitions aren't always redundant in practice if one considers performance, floating point accuracy or even sometimes divergence.
For example, foldl on infinite snoc lists is not definable in terms of foldr which diverges, though admittedly I rather think that infinite snoc lists violate all reasonable expectations of a Foldable instance.
So in many cases redundancy warnings would not be viable. The Eq situation is likely more the exception that the rule.
The SQL NULL instances that are False for both "==" and "/=" look like they could be a mistake to me, but presumably they work out OK in practice, and I would expect that e.g. the relevant `Ord` instances do return `EQ` for `compare Null Null`... Less clear is whether the non-lawful Eq defintion is in some predicably useful way essential.
-- Viktor. ------------------------------ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________
Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

Let me put it a different way: pretty much *any* function polymorphic
over Ord types will have strange, difficult to predict behavior when
given NaN. This is a *bad* thing. The last thing we need is to add
more types with unlawful Ord instances. There is no *benefit* to
giving NULL (by which I assume you mean nullPtr?) special treatment in
this regard.
On Wed, Oct 27, 2021 at 10:38 PM Keith
Sure, you can consider NULL to be the top or bottom of the lattice. But in practice what is the point of putting it in a `Set`?
You extract all the phone numbers from a DB and stick 'em in a `Set`, and every missing number is mapped to the single NULL in the `Set`? And now you know from the `Set` that at least one number was missing? — Sent from my phone with K-9 Mail.
On 28 October 2021 00:14:11 UTC, David Feuer
wrote: NULL is not like NaN. It's perfectly sensible to stick `NULL` in a `Set` or something.
On Wed, Oct 27, 2021, 8:09 PM Keith
wrote: For what it's worth, Haskell says that NaN is `GT` NaN. So maybe it would also claim than NULL is `GT` NULL.
(NaN is not `==` to NaN, and is not `<=` to NaN, so it must be GT.) — Sent from my phone with K-9 Mail.
On 27 October 2021 15:32:40 UTC, Viktor Dukhovni
wrote: On Wed, Oct 27, 2021 at 11:14:45AM -0400, Carter Schonwald wrote:
not necessarily ... there could be contradictory sets of methods! :)
like the minimal sets for Field type class, the xor would be for defining '/' in terms of reciprocal and times or vice versa (/ vs recip) and likewise (negate vs minus) etc etc
Logically redundant definitions aren't always redundant in practice if one considers performance, floating point accuracy or even sometimes divergence.
For example, foldl on infinite snoc lists is not definable in terms of foldr which diverges, though admittedly I rather think that infinite snoc lists violate all reasonable expectations of a Foldable instance.
So in many cases redundancy warnings would not be viable. The Eq situation is likely more the exception that the rule.
The SQL NULL instances that are False for both "==" and "/=" look like they could be a mistake to me, but presumably they work out OK in practice, and I would expect that e.g. the relevant `Ord` instances do return `EQ` for `compare Null Null`... Less clear is whether the non-lawful Eq defintion is in some predicably useful way essential.
-- Viktor. ________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

No, SQL NULL. An intrinsic `Nothing`.
For the most part I agree. `compare` for `Double` is should never be emulated. With luck it allows you to sort a list of `Double`s and have the NaNs randomly distributed throughout. Without luck the sort won't terminate.
—
Sent from my phone with K-9 Mail.
On 28 October 2021 05:35:17 UTC, David Feuer
Let me put it a different way: pretty much *any* function polymorphic over Ord types will have strange, difficult to predict behavior when given NaN. This is a *bad* thing. The last thing we need is to add more types with unlawful Ord instances. There is no *benefit* to giving NULL (by which I assume you mean nullPtr?) special treatment in this regard.
On Wed, Oct 27, 2021 at 10:38 PM Keith
wrote: Sure, you can consider NULL to be the top or bottom of the lattice. But in practice what is the point of putting it in a `Set`?
You extract all the phone numbers from a DB and stick 'em in a `Set`, and every missing number is mapped to the single NULL in the `Set`? And now you know from the `Set` that at least one number was missing? — Sent from my phone with K-9 Mail.
On 28 October 2021 00:14:11 UTC, David Feuer
wrote: NULL is not like NaN. It's perfectly sensible to stick `NULL` in a `Set` or something.
On Wed, Oct 27, 2021, 8:09 PM Keith
wrote: For what it's worth, Haskell says that NaN is `GT` NaN. So maybe it would also claim than NULL is `GT` NULL.
(NaN is not `==` to NaN, and is not `<=` to NaN, so it must be GT.) — Sent from my phone with K-9 Mail.
On 27 October 2021 15:32:40 UTC, Viktor Dukhovni
wrote: On Wed, Oct 27, 2021 at 11:14:45AM -0400, Carter Schonwald wrote:
not necessarily ... there could be contradictory sets of methods! :)
like the minimal sets for Field type class, the xor would be for defining '/' in terms of reciprocal and times or vice versa (/ vs recip) and likewise (negate vs minus) etc etc
Logically redundant definitions aren't always redundant in practice if one considers performance, floating point accuracy or even sometimes divergence.
For example, foldl on infinite snoc lists is not definable in terms of foldr which diverges, though admittedly I rather think that infinite snoc lists violate all reasonable expectations of a Foldable instance.
So in many cases redundancy warnings would not be viable. The Eq situation is likely more the exception that the rule.
The SQL NULL instances that are False for both "==" and "/=" look like they could be a mistake to me, but presumably they work out OK in practice, and I would expect that e.g. the relevant `Ord` instances do return `EQ` for `compare Null Null`... Less clear is whether the non-lawful Eq defintion is in some predicably useful way essential.
-- Viktor. ________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

Hi Joachim :) I will have to express a friendly but firm disagreement on the argument of a "one time cost". You will also have to open PRs for every library, change pedagogical material, broadcast those changes to developers, and provide scripts for code-modding tools in order to automate this on proprietary codebases. Side-proposal If you really want to break a bunch of things and fix mistake of the past, I would suggest to really go for the throat and have PartialEq, PartialOrd, Eq, and Ord We could finally get rid of the Eq instance for the various IEEE types like Double, and promote property testing to the wider public to verify that the laws are indeed respected by the implementations. module NewClasses where import Prelude hiding (Eq(..), Ord(..)) -- | Equality comparisons which are partial equivalence relations. class PartialEq a where (==) :: a -> a -> Bool -- | Equality comparisons which are equivalence relations. -- It is laws-only and manual implementations would be -- verified through property testing. class PartialEq a => Eq a -- | Partial order class PartialEq a => PartialOrd a where compare' :: a -> a -> Maybe Ordering (<) :: a -> a -> Bool (<=) :: a -> a -> Bool (>) :: a -> a -> Bool (>=) :: a -> a -> Bool -- | Total order class (PartialOrd a, Eq a) => Ord a where compare :: a -> a -> Ordering max :: a -> a -> a min :: a -> a -> a Cheers, Hécate Le 25/10/2021 à 15:22, Joachim Breitner a écrit :
Hi,
ah, yes, let me summarize my main motivation (perf benefits were just a side-benefit I was hoping for):
You can’t implement (/=) faster than (==) (up to, in the worst case, the cost of a single `not`, which often gets optimized away anyways).
As such, having (/=) in Eq was a (small) mistake back then, and it’s worth fixing.
There is one time cost of asking developers to _remove_ code. But code that was probably not worth writing in the first place! And I don’t blame them, the Eq class _invites_ writing that code.
Then the benefits are twofold:
* No more awkwards explanations about silly things in the likely first type class that developers care about.
* Less code to read, maintain, compile in all the libraries that _do_ define (/=) right now.
* Devs who instantiate Eq in the future will not be tricked into wondering if they need to implement (/=) and why.
So even if “helps teaching beginners” doesn’t beat “having to bug maintainers”, then maybe the second point (“saving all develpers time and effort in the future”) does?
Cheers, Joachim
-- Hécate ✨ 🐦: @TechnoEmpress IRC: Hecate WWW: https://glitchbra.in RUN: BSD

Is there a language feature that could be built that would reduce the cost of inter-package refactors like this in the future?
On October 25, 2021 4:06:46 PM UTC, "Hécate"
Hi Joachim :)
I will have to express a friendly but firm disagreement on the argument of a "one time cost". You will also have to open PRs for every library, change pedagogical material, broadcast those changes to developers, and provide scripts for code-modding tools in order to automate this on proprietary codebases.
Side-proposal
If you really want to break a bunch of things and fix mistake of the past, I would suggest to really go for the throat and have PartialEq, PartialOrd, Eq, and Ord
We could finally get rid of the Eq instance for the various IEEE types like Double, and promote property testing to the wider public to verify that the laws are indeed respected by the implementations.
module NewClasses where
import Prelude hiding (Eq(..), Ord(..))
-- | Equality comparisons which are partial equivalence relations. class PartialEq a where (==) :: a -> a -> Bool
-- | Equality comparisons which are equivalence relations. -- It is laws-only and manual implementations would be -- verified through property testing. class PartialEq a => Eq a
-- | Partial order class PartialEq a => PartialOrd a where compare' :: a -> a -> Maybe Ordering (<) :: a -> a -> Bool (<=) :: a -> a -> Bool (>) :: a -> a -> Bool (>=) :: a -> a -> Bool
-- | Total order class (PartialOrd a, Eq a) => Ord a where compare :: a -> a -> Ordering max :: a -> a -> a min :: a -> a -> a
Cheers, Hécate
Le 25/10/2021 à 15:22, Joachim Breitner a écrit :
Hi,
ah, yes, let me summarize my main motivation (perf benefits were just a side-benefit I was hoping for):
You can’t implement (/=) faster than (==) (up to, in the worst case, the cost of a single `not`, which often gets optimized away anyways).
As such, having (/=) in Eq was a (small) mistake back then, and it’s worth fixing.
There is one time cost of asking developers to _remove_ code. But code that was probably not worth writing in the first place! And I don’t blame them, the Eq class _invites_ writing that code.
Then the benefits are twofold:
* No more awkwards explanations about silly things in the likely first type class that developers care about.
* Less code to read, maintain, compile in all the libraries that _do_ define (/=) right now.
* Devs who instantiate Eq in the future will not be tricked into wondering if they need to implement (/=) and why.
So even if “helps teaching beginners” doesn’t beat “having to bug maintainers”, then maybe the second point (“saving all develpers time and effort in the future”) does?
Cheers, Joachim
-- Hécate ✨ 🐦: @TechnoEmpress IRC: Hecate WWW: https://glitchbra.in RUN: BSD
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

There are the DefaultSuperclassInstances and Intrinsic Superclasses which aim to provide in-compiler support for such transitions: https://gitlab.haskell.org/ghc/ghc/-/wikis/intrinsic-superclasses But also code-modding would benefit from being used here, and maybe retrie (https://hackage.haskell.org/package/retrie) would benefit from being used at scale? Le 25/10/2021 à 19:05, Ryan Trinkle a écrit :
Is there a language feature that could be built that would reduce the cost of inter-package refactors like this in the future?
On October 25, 2021 4:06:46 PM UTC, "Hécate"
wrote: Hi Joachim :)
I will have to express a friendly but firm disagreement on the argument of a "one time cost". You will also have to open PRs for every library, change pedagogical material, broadcast those changes to developers, and provide scripts for code-modding tools in order to automate this on proprietary codebases.
Side-proposal
If you really want to break a bunch of things and fix mistake of the past, I would suggest to really go for the throat and have PartialEq, PartialOrd, Eq, and Ord
We could finally get rid of the Eq instance for the various IEEE types like Double, and promote property testing to the wider public to verify that the laws are indeed respected by the implementations.
module NewClasses where
import Prelude hiding (Eq(..), Ord(..))
-- | Equality comparisons which are partial equivalence relations. class PartialEq a where (==) :: a -> a -> Bool
-- | Equality comparisons which are equivalence relations. -- It is laws-only and manual implementations would be -- verified through property testing. class PartialEq a => Eq a
-- | Partial order class PartialEq a => PartialOrd a where compare' :: a -> a -> Maybe Ordering (<) :: a -> a -> Bool (<=) :: a -> a -> Bool (>) :: a -> a -> Bool (>=) :: a -> a -> Bool
-- | Total order class (PartialOrd a, Eq a) => Ord a where compare :: a -> a -> Ordering max :: a -> a -> a min :: a -> a -> a
Cheers, Hécate
Le 25/10/2021 à 15:22, Joachim Breitner a écrit :
Hi, ah, yes, let me summarize my main motivation (perf benefits were just a side-benefit I was hoping for): You can’t implement (/=) faster than (==) (up to, in the worst case, the cost of a single `not`, which often gets optimized away anyways). As such, having (/=) in Eq was a (small) mistake back then, and it’s worth fixing. There is one time cost of asking developers to _remove_ code. But code that was probably not worth writing in the first place! And I don’t blame them, the Eq class _invites_ writing that code. Then the benefits are twofold: * No more awkwards explanations about silly things in the likely first type class that developers care about. * Less code to read, maintain, compile in all the libraries that _do_ define (/=) right now. * Devs who instantiate Eq in the future will not be tricked into wondering if they need to implement (/=) and why. So even if “helps teaching beginners” doesn’t beat “having to bug maintainers”, then maybe the second point (“saving all develpers time and effort in the future”) does? Cheers, Joachim
-- Hécate ✨ 🐦: @TechnoEmpress IRC: Hecate WWW: https://glitchbra.in RUN: BSD ------------------------------------------------------------------------ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
-- Hécate ✨ 🐦: @TechnoEmpress IRC: Hecate WWW:https://glitchbra.in RUN: BSD

I will also offer my own counter-proposal to intrinsic-superclasses, at https://gitlab.haskell.org/ghc/ghc/-/wikis/instance-templates. These all predate our current proposals process. I believe they were all conceived to solve the Applicative/Monad problem (for those of you who weren't around then: Applicative was not always a superclass of Monad), and I'm not sure they will work perfectly here: we might need to make a new class, say, Equality and keep the current Eq as a subclass. Do look at the proposals, but your mileage may vary. Richard
On Oct 25, 2021, at 4:50 PM, Hécate
wrote: There are the DefaultSuperclassInstances and Intrinsic Superclasses which aim to provide in-compiler support for such transitions:
https://gitlab.haskell.org/ghc/ghc/-/wikis/intrinsic-superclasses https://gitlab.haskell.org/ghc/ghc/-/wikis/intrinsic-superclasses
But also code-modding would benefit from being used here, and maybe retrie (https://hackage.haskell.org/package/retrie https://hackage.haskell.org/package/retrie) would benefit from being used at scale?
Le 25/10/2021 à 19:05, Ryan Trinkle a écrit :
Is there a language feature that could be built that would reduce the cost of inter-package refactors like this in the future?
On October 25, 2021 4:06:46 PM UTC, "Hécate"
mailto:hecate@glitchbra.in wrote: Hi Joachim :) I will have to express a friendly but firm disagreement on the argument of a "one time cost". You will also have to open PRs for every library, change pedagogical material, broadcast those changes to developers, and provide scripts for code-modding tools in order to automate this on proprietary codebases.
Side-proposal
If you really want to break a bunch of things and fix mistake of the past, I would suggest to really go for the throat and have PartialEq, PartialOrd, Eq, and Ord
We could finally get rid of the Eq instance for the various IEEE types like Double, and promote property testing to the wider public to verify that the laws are indeed respected by the implementations.
module NewClasses where
import Prelude hiding (Eq(..), Ord(..))
-- | Equality comparisons which are partial equivalence relations. class PartialEq a where (==) :: a -> a -> Bool
-- | Equality comparisons which are equivalence relations. -- It is laws-only and manual implementations would be -- verified through property testing. class PartialEq a => Eq a
-- | Partial order class PartialEq a => PartialOrd a where compare' :: a -> a -> Maybe Ordering (<) :: a -> a -> Bool (<=) :: a -> a -> Bool (>) :: a -> a -> Bool (>=) :: a -> a -> Bool
-- | Total order class (PartialOrd a, Eq a) => Ord a where compare :: a -> a -> Ordering max :: a -> a -> a min :: a -> a -> a
Cheers, Hécate
Le 25/10/2021 à 15:22, Joachim Breitner a écrit : Hi,
ah, yes, let me summarize my main motivation (perf benefits were just a side-benefit I was hoping for):
You can’t implement (/=) faster than (==) (up to, in the worst case, the cost of a single `not`, which often gets optimized away anyways).
As such, having (/=) in Eq was a (small) mistake back then, and it’s worth fixing.
There is one time cost of asking developers to _remove_ code. But code that was probably not worth writing in the first place! And I don’t blame them, the Eq class _invites_ writing that code.
Then the benefits are twofold:
* No more awkwards explanations about silly things in the likely first type class that developers care about.
* Less code to read, maintain, compile in all the libraries that _do_ define (/=) right now.
* Devs who instantiate Eq in the future will not be tricked into wondering if they need to implement (/=) and why.
So even if “helps teaching beginners” doesn’t beat “having to bug maintainers”, then maybe the second point (“saving all develpers time and effort in the future”) does?
Cheers, Joachim
-- Hécate ✨ 🐦: @TechnoEmpress IRC: Hecate WWW: https://glitchbra.in https://glitchbra.in/ RUN: BSD Libraries mailing list Libraries@haskell.org mailto:Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries -- Hécate ✨ 🐦: @TechnoEmpress IRC: Hecate WWW: https://glitchbra.in https://glitchbra.in/ RUN: BSD
Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

Thanks a lot Richard! Le 25/10/2021 à 23:02, Richard Eisenberg a écrit :
I will also offer my own counter-proposal to intrinsic-superclasses, at https://gitlab.haskell.org/ghc/ghc/-/wikis/instance-templates.
These all predate our current proposals process.
I believe they were all conceived to solve the Applicative/Monad problem (for those of you who weren't around then: Applicative was not always a superclass of Monad), and I'm not sure they will work perfectly here: we might need to make a new class, say, Equality and keep the current Eq as a subclass.
Do look at the proposals, but your mileage may vary.
Richard
On Oct 25, 2021, at 4:50 PM, Hécate
wrote: There are the DefaultSuperclassInstances and Intrinsic Superclasses which aim to provide in-compiler support for such transitions:
https://gitlab.haskell.org/ghc/ghc/-/wikis/intrinsic-superclasses
But also code-modding would benefit from being used here, and maybe retrie (https://hackage.haskell.org/package/retrie) would benefit from being used at scale?
Le 25/10/2021 à 19:05, Ryan Trinkle a écrit :
Is there a language feature that could be built that would reduce the cost of inter-package refactors like this in the future?
On October 25, 2021 4:06:46 PM UTC, "Hécate"
wrote: Hi Joachim :)
I will have to express a friendly but firm disagreement on the argument of a "one time cost". You will also have to open PRs for every library, change pedagogical material, broadcast those changes to developers, and provide scripts for code-modding tools in order to automate this on proprietary codebases.
Side-proposal
If you really want to break a bunch of things and fix mistake of the past, I would suggest to really go for the throat and have PartialEq, PartialOrd, Eq, and Ord
We could finally get rid of the Eq instance for the various IEEE types like Double, and promote property testing to the wider public to verify that the laws are indeed respected by the implementations.
module NewClasses where
import Prelude hiding (Eq(..), Ord(..))
-- | Equality comparisons which are partial equivalence relations. class PartialEq a where (==) :: a -> a -> Bool
-- | Equality comparisons which are equivalence relations. -- It is laws-only and manual implementations would be -- verified through property testing. class PartialEq a => Eq a
-- | Partial order class PartialEq a => PartialOrd a where compare' :: a -> a -> Maybe Ordering (<) :: a -> a -> Bool (<=) :: a -> a -> Bool (>) :: a -> a -> Bool (>=) :: a -> a -> Bool
-- | Total order class (PartialOrd a, Eq a) => Ord a where compare :: a -> a -> Ordering max :: a -> a -> a min :: a -> a -> a
Cheers, Hécate
Le 25/10/2021 à 15:22, Joachim Breitner a écrit :
Hi, ah, yes, let me summarize my main motivation (perf benefits were just a side-benefit I was hoping for): You can’t implement (/=) faster than (==) (up to, in the worst case, the cost of a single `not`, which often gets optimized away anyways). As such, having (/=) in Eq was a (small) mistake back then, and it’s worth fixing. There is one time cost of asking developers to _remove_ code. But code that was probably not worth writing in the first place! And I don’t blame them, the Eq class _invites_ writing that code. Then the benefits are twofold: * No more awkwards explanations about silly things in the likely first type class that developers care about. * Less code to read, maintain, compile in all the libraries that _do_ define (/=) right now. * Devs who instantiate Eq in the future will not be tricked into wondering if they need to implement (/=) and why. So even if “helps teaching beginners” doesn’t beat “having to bug maintainers”, then maybe the second point (“saving all develpers time and effort in the future”) does? Cheers, Joachim
-- Hécate ✨ 🐦: @TechnoEmpress IRC: Hecate WWW: https://glitchbra.in RUN: BSD ------------------------------------------------------------------------ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
-- Hécate ✨ 🐦: @TechnoEmpress IRC: Hecate WWW:https://glitchbra.in RUN: BSD _______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
-- Hécate ✨ 🐦: @TechnoEmpress IRC: Hecate WWW:https://glitchbra.in RUN: BSD

Hi, Am Montag, dem 25.10.2021 um 11:04 +0200 schrieb Joachim Breitner:
With this, I’d like to formally bring this change before the CLC, and ask for its support for (or explicit rejection of) this change.
as suggested by a CLC member, I moved this proposal to https://github.com/haskell/core-libraries-committee/issues/3 Cheers, Joachim -- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/

Am Mi., 20. Okt. 2021 um 17:17 Uhr schrieb Viktor Dukhovni < ietf-dane@dukhovni.org>:
For primitive types CPUs often have both '==' and '/=' instructions, and so a direct call to `(/=)` may be more efficient than calling `(not .) . (==)`. [...]
For every native code generator which is worth its salt, this shouldn't make a difference at all. Swapping the "polarity" of a condition is an extremely common transformation, which can be used for many reasons. And even if you don't have anything sophisticated, a simple peephole optimizer can get rid of the negation operation. And even if all that doesn't help: Perhaps the negation + condition are fused together in the microcode operations, depending on your CPU.

This sounds good to me. Having (/=) be a method has always seemed like a
bit of a wart, since it's hard to imagine either a reason to define it
explicitly or a reason to want it in a dictionary.
On Wed, Oct 20, 2021, 10:40 AM Joachim Breitner
Hi list,
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?
Semantically, I don’t see a reason why an instance might want to behave differently than the default method. And (in contrast to, say, Ord), I would be surprised if there are noticable performance benefits to be gained from implementing (/=) separately.
On the other hand, this is likely the first time a learner will encounter a class with default methods, and it’s awkward to explain “this mechanism is useful if you can have an optimized implementation, but, eh, here it isn’t really possible”.
If we’d design the language now, would we include (/=) as a method?
If no: would it be worth removing it?
Yes, every change is annoying, but if are going to keep using Haskell the next 30 days, it may pay off? And it might not be too bad: Remove it from base, but teach GHC to not error out if an instance defines (/=), but print a warning and otherwise ignore it. Libraries can remove it if it is defined, which is a backwards-compatible change.
WDYT? Joachim
-- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

Interestingly, I'd always assumed the answer was that all comparison
functions should return False for NaN, and mentally just assumed that would
include (/=). I never actually tested if (/=) returned False, but
apparently this isn't the case.
Prelude> (0/0) == (0/0)
False
Prelude> (0/0) /= (0/0)
True
So I can come up with no reason for (/=) to be included in the class today,
other than historical precedent based on the language report.
All the "GHC can optimize it better" things should be something that should
be able to be knocked out with relatively little fanfare, and are likely
dominated by the cost of wrapping up the method in a dictionary in the
cases where it doesn't fully inline away.
-Edward
On Wed, Oct 20, 2021 at 10:40 AM Joachim Breitner
Hi list,
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?
Semantically, I don’t see a reason why an instance might want to behave differently than the default method. And (in contrast to, say, Ord), I would be surprised if there are noticable performance benefits to be gained from implementing (/=) separately.
On the other hand, this is likely the first time a learner will encounter a class with default methods, and it’s awkward to explain “this mechanism is useful if you can have an optimized implementation, but, eh, here it isn’t really possible”.
If we’d design the language now, would we include (/=) as a method?
If no: would it be worth removing it?
Yes, every change is annoying, but if are going to keep using Haskell the next 30 days, it may pay off? And it might not be too bad: Remove it from base, but teach GHC to not error out if an instance defines (/=), but print a warning and otherwise ignore it. Libraries can remove it if it is defined, which is a backwards-compatible change.
WDYT? Joachim
-- Joachim Breitner mail@joachim-breitner.de http://www.joachim-breitner.de/
_______________________________________________ Libraries mailing list Libraries@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

Am Mi., 20. Okt. 2021 um 20:57 Uhr schrieb Edward Kmett
Interestingly, I'd always assumed the answer was that all comparison functions should return False for NaN, and mentally just assumed that would include (/=). I never actually tested if (/=) returned False, but apparently this isn't the case. [...]
NaN is the only value where the result of "x == x" is *swapped*. The reasoning behind this by Kahan himself in e.g. https://people.eecs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF: Were there no way to get rid of NaNs, they would be as useless as
Indefinites on CRAYs; as soon as one were encountered, computation would be best stopped rather than continued for an indefinite time to an Indefinite conclusion. That is why some operations upon NaNs must deliver non-NaN results. Which operations? Disagreements about some of them are inevitable, but that grants no license to resolve the disagreements by making arbitrary choices. Every real ( not logical ) function that produces the same floating-point result for all finite and infinite numerical values of an argument should yield the same result when that argument is NaN. ( Recall hypot above.) The exceptions are C predicates “ x == x ” and “ x != x ”, which are respectively 1 and 0 for every infinite or finite number x but reverse if x is Not a Number ( NaN ); these provide the only simple unexceptional distinction between NaNs and numbers in languages that lack a word for NaN and a predicate IsNaN(x). Overoptimizing compilers that substitute 1 for x == x violate IEEE 754.
IEEE 754 Wonderland... :-D
participants (18)
-
Andreas Abel
-
Brandon Allbery
-
Carter Schonwald
-
Daniel Díaz Casanueva
-
David Feuer
-
Edward Kmett
-
Henning Thielemann
-
Hécate
-
Jens Blanck
-
Joachim Breitner
-
Keith
-
Oleg Grenrus
-
Oliver Charles
-
Richard Eisenberg
-
Ryan Trinkle
-
Sven Panne
-
Tom Ellis
-
Viktor Dukhovni