If I write
foo :: (Num a) => a
foo = 42
GHC happily accepts it, but if I write
bar :: (Num a) => a
bar = (42 :: Int)
it tells me that the expected type a doesn’t match the inferred type Int. I don’t quite understand why, since Int is an instance of the class Num that a stands for.
I’m running into this same situation while trying to write a function that, boiled down to the core of the problem, looks roughly like this:
-- Note, Frob is an instance of class Frobbable
getFrobbable :: (Frobbable a) => Frob -> a
getFrobbable x = x
Is it possible to write a function like this? How can I make the result compatible with the type signature?
You are treating typeclass constraints as if they were subtype constraints. This is a common thing to do, but in fact they only coincide in the contravariant case, that is, when concerning function arguments rather than results. The signature:
Means that the caller gets to choose the type
a, provided that it is an instance ofNum. So here, the caller may choose to callbar :: Intorbar :: Double, they should all work. Sobar :: (Num a) => amust be able to construct any kind of number,bardoes not know what specific type was chosen.The contravariant case is exactly the same, it just corresponds with OO programmers’ intuition in this case. Eg.
Means that the caller gets to choose the type
a, again, and againbazdoes not know what specific type was chosen.If you need to have the callee choose the result type, just change the signature of the function to reflect this knowledge. Eg.:
Or in your
getFrobbablecase,getFrobbable :: Frob -> Frob(which makes the function trivial). Anywhere that a(Frobbable a)constraint occurs, aFrobwill satisfy it, so just sayFrobinstead.This may seem awkward, but it’s really just that information hiding happens at different boundaries in functional programming. There is a way to hide the specific choice, but it is uncommon, and I consider it a mistake in most cases where it is used.