Search code examples
classhaskellshowinstances

cannot show result from an instance function in haskell


class (Eq k, Ord k, Show k) => KEY k where
  keyBuild    :: NumId -> NumId -> NumId  -> k
  keyDummy    :: k
  keyFromList :: [NumId] -> k
  -- keyGenerate :: (DATAPOOL p) => p -> Int -> [k]  -- p = Pool k e s


newtype PrimaryKey = PK (NumId, NumId, NumId) deriving (Eq, Ord, Show) 

instance KEY PrimaryKey where
  keyBuild k0 k1 k2 = PK (k0,k1,k2)
  keyDummy          = PK (0,0,0)
  keyFromList is    = keyFromList (take 3 (is ++ (replicate 3 0)))
  keyGenerate p cnt = let
                        ks = keys p
                        pks = map (\l ->  keyFromList (randomize l))                   (replicate cnt 3)
                      in pks

in ghci I do

let k1 = keyBuild 1 2 3
let k2 = PK (1,2,3)
k1 == k2 
True
k2
PK (1,2,3)

and get True and the value of k2 as expected, but

k1
231:1: error:
    • Ambiguous type variable ‘a0’ arising from a use of ‘it’
      prevents the constraint ‘(KEY a0)’ from being solved.
      Probable fix: use a type annotation to specify what ‘a0’ should be.
      These potential instance exist:
        instance [safe] KEY PrimaryKey -- Defined at Work

Expected PK(1,2,3) PrimaryKey has deriving (Eq, Ord, Show) so what have I done wrong or missed?


Solution

  • You have not given k1 a fixed type. Because keyBuild can construct a key of any type with the right instance, it has the polymorphic type k1 :: KEY k => k. That's nice because you can then compare k1 to keys of different concrete types... in your case, you've tried it with k2 :: PrimaryKey, but you could just as well do k1 == k3 with k3 :: SomeOtherKeyType, provided SomeOtherKeyType is also an instance of the KEY class – in that case, k1 would simply also “take over” the type SomeOtherKeyType.

    The flip side is that k1 has no particular type. It may in fact be any applicable type, but how is the compiler supposed to know which you want? When using == it must be the same type on both sides, so there it's sufficient to have a concrete type on one side, but all by itself k1 is ambiguous.

    It so happens that in your module there is only one instance that matches, namely KEY PrimaryKey but in general there would be many (or zero!) such instances. It would be strange if the compiler just picked one arbitrarily, wouldn't it?

    So if you want to show k1, you need to manually pick one type. Easy enough to do, just add a local signature:

    *Main> let k1 = keyBuild 1 2 3
    *Main> :t k1
    k1 :: KEY k => k
    *Main> k1 :: PrimaryKey
    PK (1,2,3)