I’ve just published my first Elm package: malaire/elm-safe-int
SafeInt
SafeInt is 54-bit signed integer which works for full JavaScript safe integer range from - (2^53 - 1) to 2^53 - 1 (from -9007199254740991 to 9007199254740991).
-- `2^40 / 10`
SafeInt.pow SafeInt.two (SafeInt.new 40)
|> SafeInt.divBy (SafeInt.new 10)
|> SafeInt.toInt
--> Just 109951162777
SafeInt has a concept of undefined: When function result can’t be represented using an integer within safe integer range, result will be undefined which is propagated through all functions:
-- `12 / 0 * 34`
-- undefined because of division by zero
SafeInt.div (SafeInt.new 12) SafeInt.zero
|> SafeInt.mul (SafeInt.new 34)
|> SafeInt.toInt
--> Nothing
-- `2 ^ 55 / 1000`
-- undefined because safe integer range is exceeded
SafeInt.pow SafeInt.two (SafeInt.new 55)
|> SafeInt.divBy (SafeInt.new 1000)
|> SafeInt.toInt
--> Nothing
SafeInt.Unchecked
For cases where more speed is required at the cost of some lost safety and features, module SafeInt.Unchecked provides Unchecked versions of SafeInt functions which work directly on Float values.
These are still integer functions and e.g. divBy does integer division:
import SafeInt.Unchecked as Unchecked
-- `2^40 / 10`
Unchecked.pow 2 40
|> Unchecked.divBy 10
--> 109951162777
Performance
I’m not interested in doing proper benchmarking, but in some quick benchmarks:
-
SafeIntwas up to 6 times slower thanIntorFloat -
Uncheckedwas up to 3 times slower thanIntorFloat -
SafeIntwas up to 2.5 times slower thanUnchecked
Too many divisions
Ever wanted a package with 24* basic integer-division-related functions? Probably not, but here you go. 
import SafeInt.Unchecked as Unchecked
Unchecked.divMod -1234 100
--> ( -13, 66 )
-1234 |> Unchecked.quotRemBy 100
--> ( -12, -34 )
*) div, mod, quotient, remainder, divMod, quotRem, then By versions and SafeInt / Unchecked versions.
Note: Using flip or even defining a function as modBy a b = mod b a adds significant overhead on small functions like these.
When Int is not an integer
Did you know that in Elm Int can be NaN, -Infinity, +Infinity, or even a floating-point value like 1234.5 ?
But don’t worry, SafeInt handles all of these cases - it wouldn’t be Safe if it didn’t. For example SafeInt.fromInt : Int -> SafeInt is described as “Convert Int to SafeInt, rounding towards zero”, so if you give it a floating-point value as Int, it will be properly truncated to an integer. And NaN or infinities will be converted to undefined.
SafeInt.pow also truncates the result, always returning either an integer or undefined, unlike ^ operator:
$ elm repl
> round 2 ^ -1
0.5 : Int
> import SafeInt
> SafeInt.pow SafeInt.two (SafeInt.new -1)
Defined 0 : SafeInt.SafeInt
And SafeInt functions of course never crash, unlike Int functions.
More about design goals in README.