Reputation: 1729
I'm trying to understand Haskell's type system. And I came along the following:
*Main> :t ("Hello", 4)
("Hello", 4) :: Num t => ([Char], t)
*Main> :t ("Hello", 'a')
("Hello", 'a') :: ([Char], Char)
*Main> :t ("Hello", True)
("Hello", True) :: ([Char], Bool)
Why is the type for ("Hello", 4)
not denoted like the others. I would have expected it to be ("Hello", 4) :: ([Char], Num)
I already saw the =>
before. What I am wondering, is why does it make this difference?
Upvotes: 2
Views: 1788
Reputation: 54584
Number literals are a special case, the 4
could be e.g. interpreted as an Int
or as an Integer
, and the compiler won't make that choice for you, but takes a kind of "least common denominator" in the form of the common typeclass Num
(as the other answers already explain).
The additional point I try to make is if you are more specific at this point, the weirdness disappears:
-- specify Int explicitly
*Main> :t ("Hello", 4 :: Int)
("Hello", 4 :: Int) :: ([Char], Int)
-- specify Integer explicitly
*Main> :t ("Hello", 4 :: Integer)
("Hello", 4 :: Integer) :: ([Char], Integer)
-- use an Int result from an expression
*Main> :t ("Hello", length [1,2,3])
("Hello", length [1,2,3]) :: ([Char], Int)
Upvotes: 3
Reputation: 2103
Num
is a typeclass. Typeclasses are sort of (but not really) like OO interfaces. Different types implement the functions of the typeclass, allowing for polymorphism.
For example, there is a standard Eq
class, which is defined as (you can get this in ghci with :info Eq
)
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
This means that you can write functions like isMember :: (Eq a) => a -> [a] -> Bool
. This function takes something of a type a
that implements (has an instance for) Eq
and a list of that same type, and returns a boolean. This generic type signature means that you can do
elem 'c' "abcd"
elem 4 [1,2,3]
and it will compile, but that you can't do
elem 'c' [1,2,3]
Now, back to your original question.
When you type a number in ghci, it tries to give that number the most general type it can. It doesn't know whether 5
is an Int
, or an Integer
, or even a Double
. So, the literal has the type (Num t) => t
, which means "this thing can have any type t
than implements the Num
typeclass).
Different expressions will give you more specific types. For example, 5 `div` 3
has type (Integral a) => a
, since div
is a method of the Integral
class.
If you'd like to learn more about typeclasses, this is a good link.
Upvotes: 2
Reputation: 30103
That's because Num
is not a type; it is a typeclass, and Num t => someType
means that t
is some arbitrary type that is an instance of the Num
typeclass. To borrow some Java/C# terminology, you can think of Num
as an interface, and Num t => t
is a generic type with the constraint that t
must implement the Num
interface.
In general, you find the typeclass constraints on the left side of the =>
arrow, and the type body on the right side. We could have multiple class constraints, for example (Num a, Num b) => (a, b)
, which would denote the type of a tuple of two arbitrary numeric types. We can also have zero class constraints, in which case the =>
is omitted.
In Haskell the numeric literals can represent any type that is an instance of Num
. The literal 4
could denote a float or an integer, or (if you define some more exotic instances) even a function.
Upvotes: 11