Angelo.Hannes
Angelo.Hannes

Reputation: 1729

Haskell's type for Pairs

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

Answers (3)

Landei
Landei

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

krzysz00
krzysz00

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

András Kovács
András Kovács

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

Related Questions