Reputation: 6274
I am currently trying to implement a typeclass that allows me to convert any of custom types to an Integral
(those types should all be convertible to an index or a weight). In this specific case I want to use it to make sure that a musical note, on a given octave, can be converted to the index of a piano keyboard key:
import Data.Int
class Convertible a where
toIntegral :: Integral b => a -> b
data Note = Note Tone Int
instance Convertible Note where
toIntegral (Note t o) = o * 12 + toIntegral t
However the compiler complains my implementation of toIntegral
for Note
does not comply with my Convertible
typeclass.
• Couldn't match expected type ‘b’ with actual type ‘Int’
‘b’ is a rigid type variable bound by
the type signature for:
num :: forall b. Integral b => Note -> b
at src/Note.hs:11:5
• In the expression: octave * 12 + (num tone)
In an equation for ‘num’:
num (Note tone octave) = octave * 12 + (num tone)
In the instance declaration for ‘Convertible Note’
• Relevant bindings include
num :: Note -> b (bound at src/Note.hs:11:5)
As far as I understand, it considers the content of my toIntegral
function evaluates to an a -> Int
, when I constrained it to be an Integral => b -> a -> b
. Now I don't understand what's wrong, because Int
implements the Integral
typeclass, right?
Can you help me understand what I'm obviously doing wrong here?
Upvotes: 2
Views: 133
Reputation: 116139
Type variables are chosen by the caller, not by the callee. In your case, toIntegral
can not choose b
, its caller does. According to its type, all of these uses must type check
toIntegral (Note t o) :: Int
toIntegral (Note t o) :: Integer
toIntegral (Note t o) :: Word32
-- etc.
The solution is to convert Int
s as well, i.e. convert o
, so to return any integral b
, and not just an Int
.
For instance, fromIntegral o
can convert o
to any Num
type, hence any Integral
one.
Anyway, are you sure you really need that type for your toIntegral
?
Upvotes: 3