Say I want to write a function to decide whether a given integer number is prime, which type signature should I use?
isPrime :: Int -> Bool
or
isPrime :: (Integral a) => a -> Bool
What’s the difference? Is there a particular reason to choose one over the other?
If so, in which situations should I use the two respectively?
The type
Int -> Boolmeans that your function operates on values of typeInt, which are size-limited integers (the maximum size being, I believe, machine-dependent).The type
(Integral a) => a -> Boolmeans that your function operates on values of any type that has an instance of theIntegraltype class–i.e., types that behave like integers in a particular way. The main reason to chose this over a concrete type is to create a more general-purpose function.Generic forms using
Integraltend to be most useful when you need to work with integer-like types in other contexts–a good example being places where the standard library fails to do so, e.g. functions likereplicate :: Int -> a -> [a]. Code that operates on some specific integer-like type for its own purposes that wants to use that type withreplicatetherefore needs to convert toIntfirst, or importgenericReplicatefromData.List.What you might want to consider in your case is instead the type
Integer, which represents integers of arbitrary size. Since your main goal is the calculation, there’s less value to supporting arbitrary integral types.If memory serves me, the only instances of
Integralin the standard library areIntandIntegeranyhow. (EDIT: As hammar reminds me in the comments, there are also instances for fixed-size types inData.IntandData.Word. There are also foreign types likeCIntbut I was disregarding those intentionally.)