Your type signature for getVal
isn't correct, you'd like the type
getVal (Storable a, Ord a, ...) => Tag a -> a
getVal (Tag v _) = v
The reason this isn't inferred is because you can do things like
doh :: Tag a
doh = undefined
Now that a
doesn't have any constraints on it. We can do something like
getVal (doh :: Tag (IO Int)) == getVal (doh :: Tag (IO Int))
if getVal
had those constraints.
The only nonbottom instances of Tag
have your typeclass constraints on them, but this isn't enough for the typechecker since then it'd be inconsistent with bottom.
To answer the new question
When you deconstruct types like this
foo tag = let (Tag a _) = tag
(Tag b _) = tag
in a > b
GHC doesn't unify them properly. I suspect this is because the typechecker has already decided on the type of a
by the time the pattern match is reached, but with a top level match it will unify properly.
foo (Tag a _) (Tag b _) = a > b