Playing around with type-classes I came up with the seemingly innocent
class Pair p a | p -> a where
one :: p -> a
two :: p -> a
This seems to work fine, e.g.
instance Pair [a] a where
one [x,_] = x
two [_,y] = y
However I run in trouble for tuples. Even though the following definition compiles...
instance Pair (a,a) a where
one p = fst p
two p = snd p
... I can't use it as I expected:
main = print $ two (3, 4)
No instance for (Pair (t, t1) a)
arising from a use of `two' at src\Main.hs:593:15-23
Possible fix: add an instance declaration for (Pair (t, t1) a)
In the second argument of `($)', namely `two (3, 4)'
In the expression: print $ two (3, 4)
In the definition of `main': main = print $ two (3, 4)
Is there a way to define the instance correctly? Or do I have to resort to a newtype
wrapper?
Your instance works just fine, actually. Observe:
main = print $ two (3 :: Int, 4 :: Int)
This works as expected. So why doesn't it work without the type annotation, then? Well, consider the tuple's type: (3, 4) :: (Num t, Num t1) => (t, t1)
. Because numeric literals are polymorphic, nothing requires them to be the same type. The instance is defined for (a, a)
, but the existence of that instance won't tell GHC to unify the types (for a variety of good reasons). Unless GHC can deduce by other means that the two types are the same, it won't choose the instance you want, even if the two types could be made equal.
To solve your problem, you could just add type annotations, as I did above. If the arguments are coming from elsewhere it's usually unnecessary because they'll already be known to be the same type, but it gets clumsy quickly if you want to use numeric literals.
An alternative solution is to note that, because of how instance selection works, having an instance for (a, a)
means that you can't write an instance like (a, b)
as well even if you wanted to. So we can cheat a bit, to force the unification using the type class, like this:
instance (a ~ b) => Pair (a,b) a where
That needs the TypeFamilies
extension for the ~
context, I think. What this does is allow the instance to match on any tuple at first, because instance selection ignores the context. After choosing the instance, however, the a ~ b
context asserts type equality, which will produce an error if they're different but--more importantly here--will unify the type variables if possible. Using this, your definition of main
works as is, without annotations.
The problem is that a literal number has a polymorphic type. It is not obvious to the typechecker that both literals should have the same type (Int
). If you use something that is not polymorphic for your tuples, your code should work. Consider these examples:
*Main> two (3,4)
<interactive>:1:1:
No instance for (Pair (t0, t1) a0)
arising from a use of `two'
Possible fix: add an instance declaration for (Pair (t0, t1) a0)
In the expression: two (3, 4)
In an equation for `it': it = two (3, 4)
*Main> let f = id :: Int -> Int -- Force a monomorphic type
*Main> two (f 3,f 4)
4
*Main> two ('a','b')
'b'
*Main> two ("foo","bar")
"bar"
*Main> two (('a':),('b':)) "cde"
"bcde"
*Main>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With