Reputation: 504
Just for kicks, I wanted to see what would happen if I defined a function in Haskell as Int -> Int
, knowing that it would overflow and have to return an Integer
. Consider the following:
factorial :: Int -> Int
factorial n = product [1..n]
Now I know that if I run factorial 50
, I'm going to get a number outside of the 'codomain' of factorial
. Since Haskell is so strongly typed, I was hoping it would return an error. Instead GHCi returns a weird negative Int
:
ghci> factorial 50
-3258495067890909184
And note that
ghci> maxBound :: Int
9223372036854775808
since I'm running on 64 bit.
I find this behavior kind of scary: why doesn't Haskell raise an error? And why does factorial 50
return a random negative number? Any clarification would be greatly appreciated.
Upvotes: 5
Views: 3519
Reputation: 8448
The Int
type is defined as "A fixed-precision integer type with at least the range [-2^29 .. 2^29-1]." [0] The range varies by machine but you can find it with minBound
and maxBound
The reason it overflows is that it's got a fixed amount of memory allocated for it. Imagine a simpler example - a positive integer in memory whose maximum value was 7 (stored as 3 bits in memory)
0 is represented as 000, in binary
1 is represented as 001
2 is represented as 010, etc.
Note how the bit math works: when you add 1, you either turn the smallest digit from 0 to 1, or you turn it from 1 to 0 and do the same operation on the next-most-significant digit.
So for example, 011 + 1
is 100
.
Now if you naievely (as Haskell does) perform this when there's no next-most-significant digit, then it just increments as usual, but "gets its head cut off." E.g. 111 + 1
becomes 000
instead of 1000
. This is what's happening with Haskell, except that its lowest value (represented as a series of it uses its "leftmost" bit to represent +/-. You'll need to do 0
s) is its lowest negative number.(maxBound :: Int) + (maxBound :: Int) + 2
to get 0.
So similarly:
> maxBound :: Int
9223372036854775807
> (maxBound :: Int) + 1
-9223372036854775808
> (maxBound :: Int) + 2
-9223372036854775807
> let x = (maxBound :: Int) + 1 in x + x
0
Why "allow" this to happen? Simple - efficiency. It's much faster to not check for if there'll be an integer overflow. This is the reason that Integer
exists - it's unbounded, for when you think you might overflow, but you pay a price in efficiency.
[0] http://www.haskell.org/ghc/docs/latest/html/libraries/base/Data-Int.html
Upvotes: 20