A natural number ≥ 0 that has extra information about its range at compile-time
toHexChartoHexChar : Int -> CharInt between 0 & 15 is expectedtoHexChar has to handle the cases where the argument isn't between 0 & 15Maybe which will be carried throughout your program'?' or even worse '0'Int range promise of the argument is lost after the operationwith bounded-nat:
toHexChar : N (In anyMinimum_ N15 difference_) -> CharThe argument type says: Give me an integer ≥ 0 N In range
≥ 0; anyMinimum_ value allowed≤ N15n0, n1, ... which has a difference_ )Users can prove this by explicitly
using exact values
toHexChar n2 --→ 'c'
red = rgbPercent { r = n100, g = n0, b = n0 } --👍handling the possibility that a number isn't in the expected range
toPositive : Int -> Maybe (N (Min N1))
toPositive =
N.intIsAtLeast n1 >> Result.toMaybeclamping
floatPercent float =
float * 100 |> round |> N.intIn ( n0, n100 )There are more ways, but you get the idea 🙂
toDigittoDigit : Char -> Maybe IntYou might be able to do anything with this Int value, but you lost useful information.
toDigit : Char -> Maybe (N (In N0 (Add9 a_) {}))The type of an N value will reflect how much you and the compiler know
MinInN (In ... (Is ...))
factorialintFactorial : Int -> Int
intFactorial x =
case x of
0 ->
1
non0 ->
non0 * intFactorial (non0 - 1)This forms an infinite loop if we call intFactorial -1...
Let's disallow negative numbers here (& more)!
factorial : N (In min_ max_ difference_) -> N (Min N1)
factorial =
factorialBodySays: For every n ≥ 0, n! ≥ 1.
factorialBody : N (In min_ max_ difference_) -> N (Min N1)
factorialBody x =
case x |> N.isAtLeast n1 of
Err _ ->
n1 |> N.noMax
Ok n1AtLeast ->
-- n1AtLeast : N (Min N1)
-- so subtracting 1, we're still ≥ 0
factorial (n1AtLeast |> N.minSub n1)
|> N.mul n1AtLeast
factorial n4 |> N.toInt --→ 24→ You can't put a negative number in
→ We have an extra promise! every result is ≥ 1
Sadly, we need separate factorial & factorialBody because there's no support for polymorphic recursion.
But we can do even better!
!19 is already > the maximum safe Int 2^53 - 1.
safeFactorial : N (In min_ N18 difference_) -> N (Min N1)
safeFactorial =
factorialNo extra work.
keep as much type information as possible and drop it only where you need to: "Wrap early, unwrap late"
keep argument types as broad as possible
Instead of accepting only n0, n1, ... values in a range
percent :
N (In p_ atLeast_ (Is (Diff to100_ To N100) diff1_))
-> Lengthaccept values that are somewhere in a range.
percent : N (In min_ N100 difference_) -> Lengthdifference_ says that it can be exact anyway.
Or instead of
charFromCode : N (Min min_) -> Charwhich you should also never do, allow maximum-constrained and specific numbers to fit in as well:
charFromCode : N (In min_ max_ difference_) -> Chartypesafe-array shows thatN (In ...) is very useful as an indexIn, Min, Exactly can also describe a length