Haskell: Why this would lead to empty list exception?
-
29-05-2021 - |
質問
I am currently working on Project Euler for fun, and use Haskell for practicing. However, I encountered a problem, and I cannot seem to produce a smaller example, so here are the codes (for project euler 232):
buildNum (x, y) = multNum x y 1 []
where multNum num mul exp s = if (num > 10 ^ 100) then s else multNum nNum mul nexp ns
where next = (num * mul) `div` exp
ns = num:s
top = next `mod` 10
nexp = exp * 10
nNum = num + nexp * top
sumBuild (x, y) = (head (buildNum (x, y))) * length (buildNum (x, y))
Please ignore bad style here:)
If I load this and run sumBuild(7, 5) here, I would get a exception:
*** Exception: Prelude.head: empty list
But if I change sumBuild to:
sumBuild (x, y) = head (buildNum (x, y))
or
sumBuild (x, y) = length (buildNum (x, y))
then it runs fine.
This is really confusing for me, since there is no side effect and two runs produce different results (at least it seems to be such a case). What is the problem here?
If someone could edit this program into a minimum working example, then I am really grateful!
Thanks for any help!
解決
This is the hint:
> :t sumBuild
sumBuild :: (Int, Int) -> Int
> let a = 7 :: Int
> a > 10^100
True
As a suggestion try always explicitly state types of top level declarations. This helps to avoid such bugs.
他のヒント
The problem is that by multiplying with the result of length
, which is an Int
, you force all the calculations to be done with the machine-sized Int
type, leading to overflows. When you remove that, the default arbitrary-sized Integer
type is used instead.
If you add a type signature and a fromIntegral
to force the calculations to be done with Integer
, everything works as expected.
buildNum :: (Integer, Integer) -> [Integer]
buildNum (x, y) = -- same as before
sumBuild :: (Integer, Integer) -> Integer
sumBuild (x, y) = (head (buildNum (x, y))) * fromIntegral (length (buildNum (x, y)))