{-# LANGUAGE MultiParamTypeClasses #-}
class Coerce a b where coerce :: a -> b
instance Coerce a a where coerce a = a
Now, this won't work: coerce 0 :: Int
But if the instance is replace with this, the expression works:
instance a ~ b => Coerce a b where coerce x = x
Why?
I can tell you why the first one doesn't work.
Coerce could potentially be defined for any possible pair of types. coerce 0 :: Int is parsed as (coerce 0) :: Int. So you've fixed the b in coerce :: a -> b with the type annotation, but not the the a.
Numeric literals are polymorphic, so 0 has type Num a => a. That won't work; there's no instance matching Coerce a Int. There could be Coerce Double Int, Coerce Complex Int, etc, so knowing that b is Int isn't enough to infer that that 0 is an Int. We'd need to say coerce (0 :: Int) :: Int to fix both type parameters.
I believe the second one works because constraints on the instance declaration aren't used to help resolve type classes. instance a ~ b => Coerce a b is matched exactly as if you'd written instance Coerce a b. I.e. this is the most general instance possible (for the purpose of type class resolution), which well match any possible call to coerce (and so you can't write any other non-overlapping instances). The a ~ b constraint is only applied after the instance is chosen.
Since you have one instance that matches anything, there's no problem selecting an instance for coerce 0 :: Int, even though we still have the same problem of not knowing what type the 0 is. But then after instance selection, we now have the additional constraint a ~ Int, which allows unambiguous types to be assigned to everything.
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