I'm a beginner too. I've been making a little chart as I go along, which might be helpful to you:
Eq -- specifies the functions: (==) (/=)
Also, a required typeclass for a variable if:
1) you check to see if a variable is in a list with the `elem` list function
2) a variable is being pattern matched against a specific value
3) you pass a variable to another function that requires its arg to be (Eq a)
Ord -- specifies the functions: (<) (<=) (>) (>=)
(Ord implements Eq)
Num -- specifies the functions: (+) (-) (*)
but NOT (/) which is part of the Fractional typeclass
(Num implements Show, Eq)
ghci> :t (*)
(*) :: (Num a) => a -> a -> a
Integral -- specifies the functions: mod, div(integer division operator)
Both Int and Integer(= really big ints) implement the
Integral typeclass
(Integral implements Real(which implements both Ord and Num) )
Fractional -- specifies the function: (/)
Float, Double, Rational types implement Fractional
(Fractional implements Num)
When I first write my type declarations, I don't worry too much about the 'type constraints' I might need, so I might write:
myfunc :: a -> a -> a
Then after I write the function, I look around the function, and I identify what operators are being used on the a's. If a's are being added, I look at the chart and see that Num is the typeclass that has the (+) operator. If a's are being compared with <, then I look at the chart and see that Ord has the comparison operators. At that point, I add stuff to the type declaration to make sure all the necessary typeclasses are specified:
myfunc :: (Num a, Ord a) => a -> a -> a
The type 'a' stands for any type, and the type constraints, (Num a, Ord a), say, "Hold on there, not any type will do for 'a'. There's going to be some adding and comparing done with those a's, so the type of 'a' is restricted from being any type to a type that implements the functions in Num and Ord.