Eq typeclass calculation

I am writing an application similar to CRUD and have many searches for the primary key (primary keys can have different types). Therefore, I defined the following class:

{-# LANGUAGE MultiParamTypeClasses #-} class Eq b => HasPK ab where getPK :: a -> b 

Now I can write:

 import Data.Maybe lookupPK :: HasPK ab => b -> [a] -> Maybe a lookupPK s = listToMaybe . filter ((== s) . getPK) 

Now that I want to compare two things with PK, I just want to compare their PK. So, I am trying to determine this:

 {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-} instance (HasPK ab) => Eq a where (==) = (==) `on` getPK 

But now it gives me:

 src/Utils.hs:61:10: Could not deduce (HasPK a b0) … arising from the ambiguity check for an instance declaration from the context (HasPK ab) bound by an instance declaration: HasPK ab => Eq a at /home/utdemir/workspace/.../Utils.hs:61:10-28 The type variable 'b0' is ambiguous In the ambiguity check for: forall a b. HasPK ab => Eq a To defer the ambiguity check to use sites, enable AllowAmbiguousTypes In the instance declaration for 'Eq a' Compilation failed. 

Can someone explain this error to me? Am I on the right track, or is there a safer way to achieve what I want?

+7
haskell typeclass
source share
1 answer

Here you need a functional dependency: use

 class Eq b => HasPK ab | a -> b where getPK :: a -> b 

and enable all GHC extension points.

The problem is being able to have multiple PKs for the same type as in

 instance HasPK MyType Int where ... instance HasPK MyType String where ... 

Since the aforementioned double instance can be added later, code like (==) `on` getPK potentially ambiguous. Indeed, when added instances are added, it does not indicate whether to use Int PK or String .


However, an instance of type

 instance (HasPK ab) => Eq a where ... 

can cause problems because it overlaps with any other instance of Eq . Be careful. I will write instead

 equalPK :: HasPK ab => a -> a -> Bool equalPK = (==) `on` getPK 

and then provide explicit instances to all relevant types as follows:

 instance Eq MyType1 where (==) = equalPK instance Eq MyType2 where (==) = equalPK ... 

As another alternative, you can use a type family like

 class HasPK a where type PK a getPK :: a -> PK a equalPK :: Eq (PK a) => a -> a -> Bool equalPK = (==) `on` getPK instance Eq MyType1 where (==) = equalPK ... 
+9
source share

All Articles