-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Relax instances for Functor combinators; put superclasses on <class>1 to make less-breaking #10
Comments
When I rebased this, I found that |
haskellari/some#21 This is an example of an downstream library improvement that is blocked on this change.
|
The canonicity law looks good to me -- I'm actually surprised that Do we know how much of hackage this breaks? It looks like there was some effort to run this against |
Besides back-compat (just getting rid of the classes altogether would be far more breaking), the classes are still meaningful if one wants to lift a non-canonical function on the inside. I am not sure how often that is done, but it's theoretically useful enough that I don't feel inspired to rid of this.
I will try running it again. I think it was just broken then, but might work now. |
|
Thanks, I now agree that |
Are these laws unconditional or are they expecting that an underlying |
This is something I'd also love to see some impact analysis on, but I can imagine most users of the *1 classes being happy about the improvement, even if it might involve knocking out some forgotten instances. I'd also personally be happy about the fixes for |
I'm tentatively in favor of this proposal, it reflects my experience with |
@phadej could you possible please share instructions how to test changes to |
The
E.g.
I'd say it's fair to assume that
I'll try to do that asap. (They aren't complicated, but not entirely trivial). A good starting point is to have a patch applied to a version of GHC and base which has stackage snapshot (e.g. 9.0.1 or 8.10.x). Migrating stackage to ghc-head is too much work. |
Thanks for mentioning @phadej that the laws are awkward to have without the superclass. That is an added benefit I forgot. The MR had a error I think I just figured out --- a test needed to be updated, but it was in the haddock submodule so my |
I think they need not relay on a sane underlying In fact, liftEq f = and . liftA2 f
liftCompare f = foldr thenCmp EQ . liftA2 f might be valid laws too. But I don't know what the cost of those super-classes would be, and especially |
There are also wrappers in other core-ish libraries, e.g.
Would be nice if instances were uniform for these. |
Does this proposal break or not code like: class Eq1 t => Hashable1 t where ...
instance (Hashable1 f, Hashable1 g) => Hashable1 (Compose f g) where ... I have to think about this, so probably a good idea to explicitly write the answer down in the proposal. |
I'm also tentatively +1 |
@phadej just a gentle reminder about #10 (comment) @Ericson2314 any chance to test against head.hackage? |
@Bodigrim as I said, the first step is to have a patch against a GHC version which has a stackage snapshot. GHC Then the simple approach is to create a cabal package with
This approach also ignores flags as This all can be automated, by
And one general tip is to do 100 packages at the time, by first doing |
The Hashable instance is fine. See how the Eq1 instance for Compose is unaffected on the patch. I would recommend those packages adjust the definition of Hashable itself analogously, but it's not mandatory. |
Hmm I thought this testing stuff was already automated, but I guess not. |
@Ericson2314 any chance for impact assessment please? I'd like to resolve this one way or another. |
Sorry, I have not blocked out the chunk of time needed to get through that fairly manual process yet. I will try to bump this forward in my queue of things to do. |
@Ericson2314 I've created https://github.com/Bodigrim/clc-stackage, which is a meta-package for the majority of libraries in Stackage Nightly. Essentially all you need to do is to backport your changes to base-4.15, compile GHC from 9.0 branch and run |
Thanks @Bodigrim. I am hoping after haskellfoundation/tech-proposals#27 goes though this process will be quite polished, and then I will give this a shot. Separately, doing git log libraries/base/Data/Functor/Classes.hs turned up ghc/ghc@e0e03d5 which linked https://mail.haskell.org/pipermail/libraries/2015-July/026014.html It looks like these This makes me also want to just remove the
The question remains, should this be done right away, or as a successive step in a longer deprecation cycle? I propose that https://gitlab.haskell.org/ghc/ghc/-/issues/20647 / https://discourse.haskell.org/t/re-pre-pre-hftp-decoupling-base-and-ghc/4269/3 gives us an alternative.
This means we still get a smooth migration, but the benefit of making obscure stuff out of tree immediately. This woks best if https://gitlab.haskell.org/ghc/ghc/-/issues/4879 / https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0134-deprecating-exports-proposal.rst is finally implemented, so those reexports can be deprecated immediately. edit actually ghc-proposals/ghc-proposals#489 is what is needed since these are module not definition reexports, and we want |
Several instances were omitted from |
This will not work unless haskell/core-libraries-committee#10 is accepted.
Opened #84 with the migration guide. |
… Class2 to make non-breaking This change is approved by the Core Libraries commitee in haskell/core-libraries-committee#10 The first change makes the `Eq`, `Ord`, `Show`, and `Read` instances for `Sum`, `Product`, and `Compose` match those for `:+:`, `:*:`, and `:.:`. These have the proper flexible contexts that are exactly what the instance needs: For example, instead of ```haskell instance (Eq1 f, Eq1 g, Eq a) => Eq (Compose f g a) where (==) = eq1 ``` we do ```haskell deriving instance Eq (f (g a)) => Eq (Compose f g a) ``` But, that change alone is rather breaking, because until now `Eq (f a)` and `Eq1 f` (and respectively the other classes and their `*1` equivalents too) are *incomparable* constraints. This has always been an annoyance of working with the `*1` classes, and now it would rear it's head one last time as an pesky migration. Instead, we give the `*1` classes superclasses, like so: ```haskell (forall a. Eq a => Eq (f a)) => Eq1 f ``` along with some laws that canonicity is preserved, like: ```haskell liftEq (==) = (==) ``` and likewise for `*2` classes: ```haskell (forall a. Eq a => Eq1 (f a)) => Eq2 f ``` and laws: ```haskell liftEq2 (==) = liftEq1 ``` The `*1` classes also have default methods using the `*2` classes where possible. What this means, as explained in the docs, is that `*1` classes really are generations of the regular classes, indicating that the methods can be split into a canonical lifting combined with a canonical inner, with the super class "witnessing" the laws[1] in a fashion. Circling back to the pragmatics of migrating, note that the superclass means evidence for the old `Sum`, `Product`, and `Compose` instances is (more than) sufficient, so breakage is less likely --- as long no instances are "missing", existing polymorphic code will continue to work. Breakage can occur when a datatype implements the `*1` class but not the corresponding regular class, but this is almost certainly an oversight. For example, containers made that mistake for `Tree` and `Ord`, which I fixed in haskell/containers#761, but fixing the issue by adding `Ord1` was extremely *un*controversial. `Generically1` was also missing `Eq`, `Ord`, `Read,` and `Show` instances. It is unlikely this would have been caught without implementing this change. ----- [1]: In fact, someday, when the laws are part of the language and not only documentation, we might be able to drop the superclass field of the dictionary by using the laws to recover the superclass in an instance-agnostic manner, e.g. with a *non*-overloaded function with type: ```haskell DictEq1 f -> DictEq a -> DictEq (f a) ``` But I don't wish to get into optomizations now, just demonstrate the close relationship between the law and the superclass. Bump haddock submodule because of test output changing.
… Class2 to make non-breaking This change is approved by the Core Libraries commitee in haskell/core-libraries-committee#10 The first change makes the `Eq`, `Ord`, `Show`, and `Read` instances for `Sum`, `Product`, and `Compose` match those for `:+:`, `:*:`, and `:.:`. These have the proper flexible contexts that are exactly what the instance needs: For example, instead of ```haskell instance (Eq1 f, Eq1 g, Eq a) => Eq (Compose f g a) where (==) = eq1 ``` we do ```haskell deriving instance Eq (f (g a)) => Eq (Compose f g a) ``` But, that change alone is rather breaking, because until now `Eq (f a)` and `Eq1 f` (and respectively the other classes and their `*1` equivalents too) are *incomparable* constraints. This has always been an annoyance of working with the `*1` classes, and now it would rear it's head one last time as an pesky migration. Instead, we give the `*1` classes superclasses, like so: ```haskell (forall a. Eq a => Eq (f a)) => Eq1 f ``` along with some laws that canonicity is preserved, like: ```haskell liftEq (==) = (==) ``` and likewise for `*2` classes: ```haskell (forall a. Eq a => Eq1 (f a)) => Eq2 f ``` and laws: ```haskell liftEq2 (==) = liftEq1 ``` The `*1` classes also have default methods using the `*2` classes where possible. What this means, as explained in the docs, is that `*1` classes really are generations of the regular classes, indicating that the methods can be split into a canonical lifting combined with a canonical inner, with the super class "witnessing" the laws[1] in a fashion. Circling back to the pragmatics of migrating, note that the superclass means evidence for the old `Sum`, `Product`, and `Compose` instances is (more than) sufficient, so breakage is less likely --- as long no instances are "missing", existing polymorphic code will continue to work. Breakage can occur when a datatype implements the `*1` class but not the corresponding regular class, but this is almost certainly an oversight. For example, containers made that mistake for `Tree` and `Ord`, which I fixed in haskell/containers#761, but fixing the issue by adding `Ord1` was extremely *un*controversial. `Generically1` was also missing `Eq`, `Ord`, `Read,` and `Show` instances. It is unlikely this would have been caught without implementing this change. ----- [1]: In fact, someday, when the laws are part of the language and not only documentation, we might be able to drop the superclass field of the dictionary by using the laws to recover the superclass in an instance-agnostic manner, e.g. with a *non*-overloaded function with type: ```haskell DictEq1 f -> DictEq a -> DictEq (f a) ``` But I don't wish to get into optomizations now, just demonstrate the close relationship between the law and the superclass. Bump haddock submodule because of test output changing.
* Migration guide for #10 * Fix typos Thanks! Co-authored-by: mixphix <[email protected]> Co-authored-by: konsumlamm <[email protected]> * List classes * Beef up guide for #10, add more examples * Update guides/functor-combinator-instances-and-class1s.md * Update guides/functor-combinator-instances-and-class1s.md Thanks! Co-authored-by: konsumlamm <[email protected]> * Update guides/functor-combinator-instances-and-class1s.md Co-authored-by: konsumlamm <[email protected]> * Update guides/functor-combinator-instances-and-class1s.md Co-authored-by: konsumlamm <[email protected]> Co-authored-by: mixphix <[email protected]> Co-authored-by: konsumlamm <[email protected]>
… Class2 to make non-breaking This change is approved by the Core Libraries commitee in haskell/core-libraries-committee#10 The first change makes the `Eq`, `Ord`, `Show`, and `Read` instances for `Sum`, `Product`, and `Compose` match those for `:+:`, `:*:`, and `:.:`. These have the proper flexible contexts that are exactly what the instance needs: For example, instead of ```haskell instance (Eq1 f, Eq1 g, Eq a) => Eq (Compose f g a) where (==) = eq1 ``` we do ```haskell deriving instance Eq (f (g a)) => Eq (Compose f g a) ``` But, that change alone is rather breaking, because until now `Eq (f a)` and `Eq1 f` (and respectively the other classes and their `*1` equivalents too) are *incomparable* constraints. This has always been an annoyance of working with the `*1` classes, and now it would rear it's head one last time as an pesky migration. Instead, we give the `*1` classes superclasses, like so: ```haskell (forall a. Eq a => Eq (f a)) => Eq1 f ``` along with some laws that canonicity is preserved, like: ```haskell liftEq (==) = (==) ``` and likewise for `*2` classes: ```haskell (forall a. Eq a => Eq1 (f a)) => Eq2 f ``` and laws: ```haskell liftEq2 (==) = liftEq1 ``` The `*1` classes also have default methods using the `*2` classes where possible. What this means, as explained in the docs, is that `*1` classes really are generations of the regular classes, indicating that the methods can be split into a canonical lifting combined with a canonical inner, with the super class "witnessing" the laws[1] in a fashion. Circling back to the pragmatics of migrating, note that the superclass means evidence for the old `Sum`, `Product`, and `Compose` instances is (more than) sufficient, so breakage is less likely --- as long no instances are "missing", existing polymorphic code will continue to work. Breakage can occur when a datatype implements the `*1` class but not the corresponding regular class, but this is almost certainly an oversight. For example, containers made that mistake for `Tree` and `Ord`, which I fixed in haskell/containers#761, but fixing the issue by adding `Ord1` was extremely *un*controversial. `Generically1` was also missing `Eq`, `Ord`, `Read,` and `Show` instances. It is unlikely this would have been caught without implementing this change. ----- [1]: In fact, someday, when the laws are part of the language and not only documentation, we might be able to drop the superclass field of the dictionary by using the laws to recover the superclass in an instance-agnostic manner, e.g. with a *non*-overloaded function with type: ```haskell DictEq1 f -> DictEq a -> DictEq (f a) ``` But I don't wish to get into optomizations now, just demonstrate the close relationship between the law and the superclass. Bump haddock submodule because of test output changing.
… Class2 to make non-breaking This change is approved by the Core Libraries commitee in haskell/core-libraries-committee#10 The first change makes the `Eq`, `Ord`, `Show`, and `Read` instances for `Sum`, `Product`, and `Compose` match those for `:+:`, `:*:`, and `:.:`. These have the proper flexible contexts that are exactly what the instance needs: For example, instead of ```haskell instance (Eq1 f, Eq1 g, Eq a) => Eq (Compose f g a) where (==) = eq1 ``` we do ```haskell deriving instance Eq (f (g a)) => Eq (Compose f g a) ``` But, that change alone is rather breaking, because until now `Eq (f a)` and `Eq1 f` (and respectively the other classes and their `*1` equivalents too) are *incomparable* constraints. This has always been an annoyance of working with the `*1` classes, and now it would rear it's head one last time as an pesky migration. Instead, we give the `*1` classes superclasses, like so: ```haskell (forall a. Eq a => Eq (f a)) => Eq1 f ``` along with some laws that canonicity is preserved, like: ```haskell liftEq (==) = (==) ``` and likewise for `*2` classes: ```haskell (forall a. Eq a => Eq1 (f a)) => Eq2 f ``` and laws: ```haskell liftEq2 (==) = liftEq1 ``` The `*1` classes also have default methods using the `*2` classes where possible. What this means, as explained in the docs, is that `*1` classes really are generations of the regular classes, indicating that the methods can be split into a canonical lifting combined with a canonical inner, with the super class "witnessing" the laws[1] in a fashion. Circling back to the pragmatics of migrating, note that the superclass means evidence for the old `Sum`, `Product`, and `Compose` instances is (more than) sufficient, so breakage is less likely --- as long no instances are "missing", existing polymorphic code will continue to work. Breakage can occur when a datatype implements the `*1` class but not the corresponding regular class, but this is almost certainly an oversight. For example, containers made that mistake for `Tree` and `Ord`, which I fixed in haskell/containers#761, but fixing the issue by adding `Ord1` was extremely *un*controversial. `Generically1` was also missing `Eq`, `Ord`, `Read,` and `Show` instances. It is unlikely this would have been caught without implementing this change. ----- [1]: In fact, someday, when the laws are part of the language and not only documentation, we might be able to drop the superclass field of the dictionary by using the laws to recover the superclass in an instance-agnostic manner, e.g. with a *non*-overloaded function with type: ```haskell DictEq1 f -> DictEq a -> DictEq (f a) ``` But I don't wish to get into optomizations now, just demonstrate the close relationship between the law and the superclass. Bump haddock submodule because of test output changing.
… Class2 to make non-breaking This change is approved by the Core Libraries commitee in haskell/core-libraries-committee#10 The first change makes the `Eq`, `Ord`, `Show`, and `Read` instances for `Sum`, `Product`, and `Compose` match those for `:+:`, `:*:`, and `:.:`. These have the proper flexible contexts that are exactly what the instance needs: For example, instead of ```haskell instance (Eq1 f, Eq1 g, Eq a) => Eq (Compose f g a) where (==) = eq1 ``` we do ```haskell deriving instance Eq (f (g a)) => Eq (Compose f g a) ``` But, that change alone is rather breaking, because until now `Eq (f a)` and `Eq1 f` (and respectively the other classes and their `*1` equivalents too) are *incomparable* constraints. This has always been an annoyance of working with the `*1` classes, and now it would rear it's head one last time as an pesky migration. Instead, we give the `*1` classes superclasses, like so: ```haskell (forall a. Eq a => Eq (f a)) => Eq1 f ``` along with some laws that canonicity is preserved, like: ```haskell liftEq (==) = (==) ``` and likewise for `*2` classes: ```haskell (forall a. Eq a => Eq1 (f a)) => Eq2 f ``` and laws: ```haskell liftEq2 (==) = liftEq1 ``` The `*1` classes also have default methods using the `*2` classes where possible. What this means, as explained in the docs, is that `*1` classes really are generations of the regular classes, indicating that the methods can be split into a canonical lifting combined with a canonical inner, with the super class "witnessing" the laws[1] in a fashion. Circling back to the pragmatics of migrating, note that the superclass means evidence for the old `Sum`, `Product`, and `Compose` instances is (more than) sufficient, so breakage is less likely --- as long no instances are "missing", existing polymorphic code will continue to work. Breakage can occur when a datatype implements the `*1` class but not the corresponding regular class, but this is almost certainly an oversight. For example, containers made that mistake for `Tree` and `Ord`, which I fixed in haskell/containers#761, but fixing the issue by adding `Ord1` was extremely *un*controversial. `Generically1` was also missing `Eq`, `Ord`, `Read,` and `Show` instances. It is unlikely this would have been caught without implementing this change. ----- [1]: In fact, someday, when the laws are part of the language and not only documentation, we might be able to drop the superclass field of the dictionary by using the laws to recover the superclass in an instance-agnostic manner, e.g. with a *non*-overloaded function with type: ```haskell DictEq1 f -> DictEq a -> DictEq (f a) ``` But I don't wish to get into optomizations now, just demonstrate the close relationship between the law and the superclass. Bump haddock submodule because of test output changing.
… Class2 to make non-breaking This change is approved by the Core Libraries commitee in haskell/core-libraries-committee#10 The first change makes the `Eq`, `Ord`, `Show`, and `Read` instances for `Sum`, `Product`, and `Compose` match those for `:+:`, `:*:`, and `:.:`. These have the proper flexible contexts that are exactly what the instance needs: For example, instead of ```haskell instance (Eq1 f, Eq1 g, Eq a) => Eq (Compose f g a) where (==) = eq1 ``` we do ```haskell deriving instance Eq (f (g a)) => Eq (Compose f g a) ``` But, that change alone is rather breaking, because until now `Eq (f a)` and `Eq1 f` (and respectively the other classes and their `*1` equivalents too) are *incomparable* constraints. This has always been an annoyance of working with the `*1` classes, and now it would rear it's head one last time as an pesky migration. Instead, we give the `*1` classes superclasses, like so: ```haskell (forall a. Eq a => Eq (f a)) => Eq1 f ``` along with some laws that canonicity is preserved, like: ```haskell liftEq (==) = (==) ``` and likewise for `*2` classes: ```haskell (forall a. Eq a => Eq1 (f a)) => Eq2 f ``` and laws: ```haskell liftEq2 (==) = liftEq1 ``` The `*1` classes also have default methods using the `*2` classes where possible. What this means, as explained in the docs, is that `*1` classes really are generations of the regular classes, indicating that the methods can be split into a canonical lifting combined with a canonical inner, with the super class "witnessing" the laws[1] in a fashion. Circling back to the pragmatics of migrating, note that the superclass means evidence for the old `Sum`, `Product`, and `Compose` instances is (more than) sufficient, so breakage is less likely --- as long no instances are "missing", existing polymorphic code will continue to work. Breakage can occur when a datatype implements the `*1` class but not the corresponding regular class, but this is almost certainly an oversight. For example, containers made that mistake for `Tree` and `Ord`, which I fixed in haskell/containers#761, but fixing the issue by adding `Ord1` was extremely *un*controversial. `Generically1` was also missing `Eq`, `Ord`, `Read,` and `Show` instances. It is unlikely this would have been caught without implementing this change. ----- [1]: In fact, someday, when the laws are part of the language and not only documentation, we might be able to drop the superclass field of the dictionary by using the laws to recover the superclass in an instance-agnostic manner, e.g. with a *non*-overloaded function with type: ```haskell DictEq1 f -> DictEq a -> DictEq (f a) ``` But I don't wish to get into optomizations now, just demonstrate the close relationship between the law and the superclass. Bump haddock submodule because of test output changing.
This will not work unless haskell/core-libraries-committee#10 is accepted.
This mirrors a corresponding change to the `Show1` and `Show2` classes in `base`—see haskell/core-libraries-committee#10. A consequence of this change is that all `TextShow1` instances now require corresponding `TextShow` instances. This affects the `TextShow.Generic` module, as we now have to define a `TextShow` instance for `FromGeneric1`. Similarly, all `TextShow2` instances now require corresponding `TextShow` and `TextShow1` instances. Fixes #56.
This mirrors a corresponding change to the `Show1` and `Show2` classes in `base`—see haskell/core-libraries-committee#10. A consequence of this change is that all `TextShow1` instances now require corresponding `TextShow` instances. This affects the `TextShow.Generic` module, as we now have to define a `TextShow` instance for `FromGeneric1`. Similarly, all `TextShow2` instances now require corresponding `TextShow` and `TextShow1` instances. Fixes #56.
It appears there are some packages on stackage which need changes due to this proposal but weren't flagged in the impact assessment. Do you know why they were missed? For example:
|
Because impact assessment is done in june of 2022? And probably against older Stackage LTS, which had different package set or/and older major versions. In case of --- a/Control/Category/Dual.hs
+++ b/Control/Category/Dual.hs
@@ -1,7 +1,7 @@
{-# LANGUAGE DerivingVia #-}
module Control.Category.Dual where
-import Prelude (Eq, Ord, Read, Show, Bounded, ($))
+import Prelude (Eq ((==)), Ord (compare), Read, Show, Bounded, ($))
import Control.Category
import Data.Bifunctor
@@ -20,6 +20,9 @@ instance Category k => Category (Dual k) where
id = Dual id
Dual f . Dual g = Dual (g . f)
+instance (Eq2 k, Eq a) => Eq1 (Dual k a) where liftEq f (Dual x) (Dual y) = liftEq2 f (==) x y
+instance (Ord2 k, Ord a) => Ord1 (Dual k a) where liftCompare f (Dual x) (Dual y) = liftCompare2 f compare x y
+
instance Eq2 k => Eq2 (Dual k) where liftEq2 f g (Dual x) (Dual y) = liftEq2 g f x y
instance Ord2 k => Ord2 (Dual k) where liftCompare2 f g (Dual x) (Dual y) = liftCompare2 g f x y
@@ -31,6 +34,9 @@ instance Show2 k => Show2 (Dual k) where
liftShowsPrec2 asp asl bsp bsl n =
showsUnaryWith (liftShowsPrec2 bsp bsl asp asl) "Dual" n . dual
+instance Bifunctor k => Functor (Dual k a) where
+ fmap f = Dual . bimap f id . dual
+
instance Bifunctor k => Bifunctor (Dual k) where
bimap f g = Dual . bimap g f . dual The problem is that there is no classes like class FunctorOn2 p where
anotherFirst :: (a -> b) -> p a x -> p b x so constraints for missing superclasses ( For the reference For diff --git a/src/Test/StateMachine/Types/References.hs b/src/Test/StateMachine/Types/References.hs
index 9e4200d..89b59da 100644
--- a/src/Test/StateMachine/Types/References.hs
+++ b/src/Test/StateMachine/Types/References.hs
@@ -81,6 +81,8 @@ instance Ord1 Symbolic where
data Concrete a where
Concrete :: Typeable a => a -> Concrete a
+deriving stock instance Eq a => Eq (Concrete a)
+deriving stock instance Ord a => Ord (Concrete a)
deriving stock instance Show a => Show (Concrete a)
instance Show1 Concrete where Especially the latter case is a problem of rolling stone uphill: without enforcement to have |
|
I'm trying to summarise the state of this proposal as part of my volunteering effort to track the progress of all
Please, let me know if you find any mistakes 🙂 |
Implementation https://gitlab.haskell.org/ghc/ghc/-/merge_requests/4727
The first change makes the
Eq
,Ord
,Show
, andRead
instances forSum
,Product
, andCompose
match those for:+:
,:*:
, and:.:
. These have the proper flexible contexts that are exactly what the instance needs:For example, instead of
we do
But, that change alone is rather breaking, because until now
Eq (f a)
andEq1 f
(and respectively the other classes and their*1
equivalents too) are incomparable constraints. This has always been an annoyance of working with the*1
classes, and now it would rear it's head one last time as an pesky migration.Instead, we give the
*1
classes superclasses, like so:along with some laws that canonicity is preserved, like:
and likewise for
*2
classesalong with some laws that canonicity is preserved, like:
The
*1
classes also have default methods using the*2
classes where possible.What this means, as explained in the docs in my implementation, is that
*1
classes really are generations of the regular classes, indicating that the methods can be split into a canonical lifting combined with a canonical inner, with the super class "witnessing" the laws[1] in a fashion.Circling back to the pragmatics of migrating, note that the superclass means evidence for the old
Sum
,Product
, andCompose
instances is (more than) sufficient, so breakage is less likely --- as long no instances are "missing", existing polymorphic code will continue to work.Breakage can occur when a datatype implements the
*1
class but not the corresponding regular class, but this is almost certainly an oversight. For example, containers made that mistake forTree
andOrd
, which I fixed in haskell/containers#761, but fixing the issue by addingOrd1
was extremely uncontroversial.[1]: In fact, someday, when the laws are part of the language and not
only documentation, we might be able to drop the superclass field of the
dictionary by using the laws to recover the superclass in an
instance-agnostic manner, e.g. with a non-overloaded function with
type:
But I don't wish to get into optomizations now, just demonstrate the
close relationship between the law and the superclass.
The text was updated successfully, but these errors were encountered: