Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Define multiple instances simultaneously

Idea from https://gist.github.com/Icelandjack/e1ddefb0d5a79617a81ee98c49fbbdc4

Works great for concrete types

data A = MkA

instance (Eq & Ord & Show & Arbitrary) A where
  (==) :: A -> A -> Bool
  MkA == MkA = True
  
  compare :: A -> A -> Ordering
  MkA `compare` MkA = EQ
  
  show :: A -> String
  show MkA = "MkA"
  
  arbitrary :: Gen A
  arbitrary = pure MkA
@Icelandjack

This comment has been minimized.

Copy link
Owner Author

@Icelandjack Icelandjack commented May 27, 2017

Not so nice for type constructors

newtype B a = MkB a

type EOSA = Eq & Ord & Show & Arbitrary

instance EOSA b => EOSA (B b) where
  (==) :: B b -> B b -> Bool
  MkB a == MkB b = a `compare` b
  
  compare :: B b -> B b -> Ordering
  MkB a `compare` MkB b = a `compare` b
  
  show :: B b -> String
  show (MkB b) = "MkB " ++ show b
  
  arbitrary :: Gen (B b)
  arbitrary = MkB <$> arbitrary

Now all of a sudden (==) @(B _) :: EOSA b => B b -> B b -> Bool which is ridiculous. We should be able to tailor the context to each method or group of methods.

@Icelandjack

This comment has been minimized.

Copy link
Owner Author

@Icelandjack Icelandjack commented May 27, 2017

It could be magic... (inferred from methods, and then crushed together) or determined from explicit instance sigs (and then crushed)

instance EOSA (B b) where
  (==) :: Eq b => B b -> B b -> Bool
  MkB a == MkB b = a == b

  compare :: B b -> B b -> Ordering
  MkB a `compare` MkB b = a `compare` b

  show :: Show b => B b -> String
  show (MkB b) = "MkB " ++ show b 
...

and the methods have the same constraints as provided, and the instances become

instance Eq        b => Eq        (B b)
instance Ord       b => Ord       (B b)
instance Show      b => Show      (B b)
instance Arbitrary b => Arbitrary (B b)
@Icelandjack

This comment has been minimized.

Copy link
Owner Author

@Icelandjack Icelandjack commented May 27, 2017

So if inferred from method's required constraints, then we have

instance EOSA (B b) where
  a == b = compare a b == EQ
  MkB a `compare` MkB b = compare a b

resulting in

instance Ord a => Eq  (B b)
instance Ord a => Ord (B b)
@Icelandjack

This comment has been minimized.

Copy link
Owner Author

@Icelandjack Icelandjack commented May 27, 2017

We merge the contexts of methods from the same type class just as usual

class  EQ a where  eq :: a -> a -> Bool
class NEQ a where neq :: a -> a -> Bool

newtype C a = C a
  deriving (EQ, NEQ)

instance (EQ a, NEQ a) => Eq (C a) where
  (==) = eq
  (/=) = neq
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment