Surprising behaviour of fromEnum Natural
Summary
fromEnum
for Natural
returns an Int
that ranges over the whole range of minBound :: Int
to maxBound :: Int
, just as Integer
does. Except for the range of numbers 2^63 to 2^64-1 inclusive (on a 64-bit machine): for that range, it returns an error. This could be very surprising to someone who did not test the behaviour with those specific numbers. They might just have tested with even larger numbers, and noticed that that works just fine.
This behaviour seems to have been introduced in commit fe770c21, before that it seems an error was thrown for anything that couldn't be represented exactly (2^63 and onwards on a 64-bit machine) as can be seen here.
Steps to reproduce
$ ghci
GHCi, version 9.3.20210825: https://www.haskell.org/ghc/ :? for help
ghci> import Numeric.Natural
ghci> fromEnum (2^62 :: Natural)
4611686018427387904
ghci> fromEnum (2^63 :: Natural)
*** Exception: fromEnum: out of Int range
ghci> fromEnum (2^64 :: Natural)
0
ghci> fromEnum (2^64 + 2^63 :: Natural)
-9223372036854775808
ghci> fromEnum (2^65 - 42 :: Natural)
-42
ghci> fromEnum (2^65 :: Natural)
0
Expected behavior
Patch 1 returns an error when the value isn't representable exactly, the situation I think we had before fe770c21. It behaves as follows:
$ ghci
GHCi, version 9.3.20210825: https://www.haskell.org/ghc/ :? for help
ghci> import Numeric.Natural
ghci> fromEnum (2^62 :: Natural)
4611686018427387904
ghci> fromEnum (2^63 :: Natural)
*** Exception: fromEnum: out of Int range
ghci> fromEnum (2^64 :: Natural)
*** Exception: fromEnum: out of Int range
ghci> fromEnum (2^64 + 2^63 :: Natural)
*** Exception: fromEnum: out of Int range
ghci> fromEnum (2^65 - 42 :: Natural)
*** Exception: fromEnum: out of Int range
ghci> fromEnum (2^65 :: Natural)
*** Exception: fromEnum: out of Int range
ghci>
Patch 2 always returns a value. The behaviour is as follows:
$ ghci
GHCi, version 9.3.20210825: https://www.haskell.org/ghc/ :? for help
ghci> import Numeric.Natural
ghci> fromEnum (2^62 :: Natural)
4611686018427387904
ghci> fromEnum (2^63 :: Natural)
-9223372036854775808
ghci> fromEnum (2^64 :: Natural)
0
ghci> fromEnum (2^64 + 2^63 :: Natural)
-9223372036854775808
ghci> fromEnum (2^65 - 42 :: Natural)
-42
ghci> fromEnum (2^65 :: Natural)
0
Possible fixes
Patch 1:
diff --git a/libraries/base/GHC/Enum.hs b/libraries/base/GHC/Enum.hs
index 02b3d0e784..2765dcd265 100644
--- a/libraries/base/GHC/Enum.hs
+++ b/libraries/base/GHC/Enum.hs
@@ -967,12 +967,10 @@ instance Enum Natural where
| i >= 0 = naturalFromWord# (int2Word# i#)
| otherwise = errorWithoutStackTrace "toEnum: unexpected negative Int"
- fromEnum (NS w)
- | i >= 0 = i
- | otherwise = errorWithoutStackTrace "fromEnum: out of Int range"
+ fromEnum (NS w) | i >= 0 = i
where
i = I# (word2Int# w)
- fromEnum n = fromEnum (integerFromNatural n)
+ fromEnum _ = errorWithoutStackTrace "fromEnum: out of Int range"
enumFrom x = enumDeltaNatural x 1
enumFromThen x y
Patch 2:
diff --git a/libraries/base/GHC/Enum.hs b/libraries/base/GHC/Enum.hs
index 02b3d0e784..e2858f6fb9 100644
--- a/libraries/base/GHC/Enum.hs
+++ b/libraries/base/GHC/Enum.hs
@@ -967,9 +967,9 @@ instance Enum Natural where
| i >= 0 = naturalFromWord# (int2Word# i#)
| otherwise = errorWithoutStackTrace "toEnum: unexpected negative Int"
- fromEnum (NS w)
+ fromEnum n@(NS w)
| i >= 0 = i
- | otherwise = errorWithoutStackTrace "fromEnum: out of Int range"
+ | otherwise = fromEnum (integerFromNatural n)
where
i = I# (word2Int# w)
fromEnum n = fromEnum (integerFromNatural n)
Environment
- GHC version used:
master
- Operating System: Ubuntu 18.04
- System Architecture: amd64
Thanks for creating and maintaining Haskell!