Re: Eq1, Ord1, Show1: move from eq1, compare1, showsPrec1 to liftEq, liftCompare, liftShowsPrec

In transformers-0.5 it seems that I have to implement Eq1, Ord1, Show1 instances manually.
For better or worse, that is indeed the case. Before, Eq1/Ord1/Read1/Show1 relied on the type argument having an implicit dictionary to use, which is why you could get away with piggybacking off of your existing Eq/Ord/Read/Show instances. Now, Eq1, Eq2, and friends rely on explicit dictionary passing, so they're strictly more powerful than Eq et al.
Is there some assistance to define eq1 and compare1 for an ADT?
At the moment, no. Sometime in the future, I want to do two things which should make the situation a little better: 1. Write a language extension to derive Eq1/Eq2/etc. It definitely won't be making it into GHC 8.0 due to time constraints, plus I need to propose a design. And... 2. ...I should write a Template Haskell shim which implements the proposed design. I already have a package called deriving-compat [1] which has done this (but only for recent changes to -XDeriveFoldable), so that would probably be a logical place to put it. Until then, if you need to hand-roll Eq1/Eq2/etc. instances that align with your Eq instances, I'd recommend copying the output of ghc -ddump-deriv, copying the generated instances, and replacing the necessary calls with dictionary arguments. For example, given this instance: data Example a = Example Int a deriving Eq Compiling with ghc -ddump-deriv gives you this (after some code cleanup): instance Eq a => Eq (Example a) where Example i1 a1 == Example i2 a2 = i1 == i2 && a1 == a2 e1 /= e2 = not (e1 == e2) Then, you can create an Eq1 instance with some minor adjustments: instance Eq1 Example where liftEq eq (Example i1 a1) (Example i2 a2) = i1 == i2 && eq a1 a2
What are the use cases where eq1 was not powerful enough and liftEq is needed?
The previous type signature of Data.Functor.Classes in transformers-0.4 resulted in some awkward instances, a prime example being found in Data.Functor.Compose: import Data.Functor.Classes(Eq1(..)) newtype Compose f g a = Compose (f (g a)) instance (Functor f, Eq1 f, Eq1 g, Eq a) => Eq (Compose f g a) where Compose x == Compose y = eq1 (fmap Apply x) (fmap Apply y) instance (Functor f, Eq1 f, Eq1 g) => Eq1 (Compose f g) where eq1 = (==) newtype Apply g a = Apply (g a) instance (Eq1 g, Eq a) => Eq (Apply g a) where Apply x == Apply y = eq1 x y This is a super-kludgey instance because it requires f to be a Functor instance. In general, having these Functor constraints pop up everywhere results in less efficient instances, and it won't work at all for many GADT-like constructions that can't have legal Functor instances. With the new API, there is no need for these Functor constraints: import Data.Functor.Classes(Eq1(..)) newtype Compose f g a = Compose (f (g a)) instance (Eq1 f, Eq1 g) => Eq1 (Compose f g) where liftEq eq (Compose x) (Compose y) = liftEq (liftEq eq) x y Ryan S. ----- [1] http://hackage.haskell.org/package/deriving-compat

On Mon, 11 Jan 2016, Ryan Scott wrote:
What are the use cases where eq1 was not powerful enough and liftEq is needed?
The previous type signature of Data.Functor.Classes in transformers-0.4 resulted in some awkward instances, a prime example being found in Data.Functor.Compose:
import Data.Functor.Classes(Eq1(..))
newtype Compose f g a = Compose (f (g a)) instance (Functor f, Eq1 f, Eq1 g, Eq a) => Eq (Compose f g a) where Compose x == Compose y = eq1 (fmap Apply x) (fmap Apply y) instance (Functor f, Eq1 f, Eq1 g) => Eq1 (Compose f g) where eq1 = (==)
newtype Apply g a = Apply (g a) instance (Eq1 g, Eq a) => Eq (Apply g a) where Apply x == Apply y = eq1 x y
Btw. I already defined my own Apply type (I called it Wrap) because I found it useful. Would you mind to export it from 'transformers'?

Btw. I already defined my own Apply type (I called it Wrap) because I found it useful. Would you mind to export it from 'transformers'?
You're talking about this transformers issue [1], correct? If so, I doubt that's ever going to happen. The changes to Eq1/Ord1/Read1/Show1 rendered Apply obsolete, since it's no longer necessary to use Apply to define Eq1 instances for nested applications of a type parameter of kind * -> *. Not only that, but there are a couple of datatypes in transformers that already resolve their Eq instances in an identical way to how Apply would (for instance, IdentityT, which is isomorphic to Apply [2]). Ryan S. ----- [1] http://hub.darcs.net/ross/transformers/issue/8 [2] http://hackage.haskell.org/package/transformers-0.5.0.0/docs/src/Control-Mon...

On Mon, 11 Jan 2016, Ryan Scott wrote:
Not only that, but there are a couple of datatypes in transformers that already resolve their Eq instances in an identical way to how Apply would (for instance, IdentityT, which is isomorphic to Apply [2]).
If IdentityT is my desired Apply/Wrap then I am happy.

me again ... On Mon, 11 Jan 2016, Ryan Scott wrote:
You're talking about this transformers issue [1], correct?
yes
Not only that, but there are a couple of datatypes in transformers that already resolve their Eq instances in an identical way to how Apply would (for instance, IdentityT, which is isomorphic to Apply [2]).
One of my applications is to use functor things in a Set, which requires Ord constraint. With IdentityT I can write: class Ord1 f => Elem f where method :: ... transform :: (Elem f, Ord a) => Set (IdentityT f a) -> Set (IdentityT f a) Without IdentityT I would have constraint (Ord (f a)).
participants (2)
-
Henning Thielemann
-
Ryan Scott