Safe Maths
This example shows how to perform calculations while avoiding overflows.
For example; +
actually uses Num.add
, which can crash if the bytes of the result can not fit in the provided type:
» Num.maxU64 + Num.maxU64 This Roc code crashed with: "Integer addition overflowed!" * : U64
If you want to avoid a program-ending crash, you can instead use:
» Num.addChecked Num.maxU64 Num.maxU64 Err Overflow : Result U64 [Overflow]
That would allow you to display a clean error to the user or handle the failure in an intelligent way.
Use Checked
math functions if reliability is important for your application.
For a realistic demonstration, we will use Checked
math functions to calculate the variance of a population.
The variance formula is: σ² = ∑(X - µ)² / N
where:
σ²
= varianceX
= each elementµ
= mean of elementsN
= length of list
Code
app [main] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br" } import cli.Stdout ## Safely calculates the variance of a population. ## ## variance formula: σ² = ∑(X - µ)² / N ## ## σ² = variance ## X = each element ## µ = mean of elements ## N = length of list ## ## Performance note: safe or checked math prevents crashes but also runs slower. ## safeVariance : List (Frac a) -> Result (Frac a) [EmptyInputList, Overflow] safeVariance = \maybeEmptyList -> # Check length to prevent DivByZero when List.len maybeEmptyList is 0 -> Err EmptyInputList _ -> nonEmptyList = maybeEmptyList n = nonEmptyList |> List.len |> Num.toFrac mean = nonEmptyList # sum of all elements: |> List.walkTry 0.0 (\state, elem -> Num.addChecked state elem) |> Result.map (\x -> x / n) nonEmptyList |> List.walkTry 0.0 (\state, elem -> mean |> Result.try (\m -> Num.subChecked elem m) # X - µ |> Result.try (\y -> Num.mulChecked y y) # ² |> Result.try (\z -> Num.addChecked z state)) # ∑ |> Result.map (\x -> x / n) main = varianceResult = [46, 69, 32, 60, 52, 41] |> safeVariance |> Result.map Num.toStr |> Result.map (\v -> "σ² = $(v)") outputStr = when varianceResult is Ok str -> str Err EmptyInputList -> "Error: EmptyInputList: I can't calculate the variance over an empty list." Err Overflow -> "Error: Overflow: When calculating the variance, a number got too large to store in the available memory for the type." Stdout.line outputStr expect (safeVariance []) == Err EmptyInputList expect (safeVariance [0]) == Ok 0 expect (safeVariance [100]) == Ok 0 expect (safeVariance [4, 22, 99, 204, 18, 20]) == Ok 5032.138888888888888888 expect (safeVariance [46, 69, 32, 60, 52, 41]) == Ok 147.666666666666666666
Output
Run this from the directory that has main.roc
in it:
$ roc main.roc σ² = 147.666666666666666666
Run unit tests with roc test main.roc