fromIntegral is dangerous
Clarification and motivation
fromIntegral
is notorious for being involved in hard-to-find bugs:
-
In #302 (closed), it silently narrowed
Int64
s toWord32
s, truncating the upper bits, which made somemutez
balances appear smaller than they actually were. -
This libraries mailing list thread contains multiple other examples of
fromIntegral
causing havoc.
We should either replace our uses of fromIntegral
with something better, or at least implement checks that any uses of fromIntegral
don't lead to unintended results.
There are multiple libraries that offer safer alternatives:
-
base
containsfromIntegralSized :: (Integral a, Integral b, Bits a, Bits b) => a -> Maybe b
which signals any overflows or underflows withNothing
. -
int-cast
providesintCast :: (Integral a, Integral b, IsIntSubType a b ~ True) => a -> b
, which prevents any narrowing. -
hw-int
contains lots of tools for integer conversions, notably theNarrow
andWiden
classes. -
basement
contains theIntegralDownsize
andIntegralUpsize
classes.
It is also possible to annotate fromIntegral
with TypeApplications
to make it clear whether it is used for widening or narrowing. E.g. fromIntegral @Int32 @Int64
is a safe widening operation.
Acceptance criteria
- Any remaining uses of
fromIntegral
are obviously correct and safe. - There are checks, e.g. via
hlint
, that prevent any dangerous uses offromIntegral
.
Edited by Simon Jakobi