Consider the following Haskell function:
sign a
| a < 0 = (-1)
| a > 0 = 1
| otherwise = 0
When I load this into ghci I expected :t sign
to be:
sign :: (Num a, Ord a) => a -> Integer
Instead it inferred it as:
*Main> :t sign
sign :: (Num a1, Num a, Ord a1) => a1 -> a
Similarly, if I ask for the type of the integer 5
, I expected Integer
, but instead I got
*Main> :t 5
5 :: Num a => a
There's something I am not understanding about Haskell's types. The thing is, if all I know about the return type of sign
is that it is an instance of the Num
typeclass, then I should not be able to pass its return value into this function:
double :: Integer -> Integer
double x = x * 2
That is, my double
function requires an Integer
, not just any instance of Num
.
Yet, the following works just fine:
*Main> double (sign 5.5)
2
What is it that I am mis-understanding about Haskell's type system?
Num is a typeclass — a group of types — which includes all types which are regarded as numbers. The (Num a) => part of the signature restricts a to number types – or, in Haskell terminology, instances of Num .
Type inference is the process by which Haskell 'guesses' the types for variables and functions, without you needing to specify these types explicitly. Many functional languages feature type inference. There is lots of theory behind type inference — Hindley-Milner type systems and Unification.
Haskell has two integral types, namely Int and Integer . Int is the type of limited-precision integers; this means that there is a smallest integer of type Int , namely minBound , and a greatest integer of type Int , namely maxBound .
In Haskell, every statement is considered as a mathematical expression and the category of this expression is called as a Type. You can say that "Type" is the data type of the expression used at compile time.
In Haskell, if a function returns type x
is its result, that means that the caller can choose what x
should be, not the function. Rather, the function must be able to return any possible type.
Your sign
can return any type of data - including Integer
. The double
function wants an Integer
, so that's just fine - sign
can return that.
Another part of the puzzle you may not be aware of: In Java, 2
has type int
and 2.0
has type double
. But in Haskell, 2
has type Num x => x
- in other words, any possible number type. (Also 2.0
has type Fractional x => x
, which is a similar deal.)
The thing is, if all I know about the return type of 'sign' is that it is an instance of the
Num
typeclass, then I should not be able to pass its return value into this function:
Right, if that were all that you knew, you couldn't pass it to double
.
But the type
sign :: (Num a1, Num a, Ord a1) => a1 -> a
means that the result type of sign
is whichever Num
type the caller demands. Type variables in type signatures are (implicitly) universally quantified, not existentially, like for e.g. Java interfaces.
sign
can produce a return value of arbitrary type, subject to the restriction it be an instance of Num
, and the type it returns is determined by the calling context.
If the caller wants an Integer
, it gets one. If it wants a Double
, no problem either.
I forgot to mention initially:
Similarly, if I ask for the type of the integer 5, I expected "Integer", but instead I got
*Main> :t 5
5 :: Num a => a
Numeric literals are polymorphic, an integer literal stands for fromInteger value
, and a fractional literal for fromRational value
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With