Type-safe natural numbers (>= 0), ensuring that a Nat is in a range at compile-time:
toHexChar : Nat (ArgIn min Nat15 maybeN) -> CharNo number below 0 or above 15 can be passed in as an argument!
elm install lue-bird/elm-typed-value
elm install lue-bird/elm-bounded-natimport Nat exposing (Nat, Min, In, N, Is, To, ArgIn)
import NNats exposing (..)
-- (..) is nat0 to nat160
import InNat
import MinNat
import TypeNats exposing (..)
-- (..) is Nat0 to Nat160 & Nat1Plus to Nat160Plus
import Typed exposing (val, val2)rgb : Float -> Float -> Float -> ColorThis is common, but
Float between 0 & 1 is wantedrgbPer100 :
Nat (ArgIn redMin Nat100 redMaybeN)
-> Nat (ArgIn greenMin Nat100 greenMaybeN)
-> Nat (ArgIn blueMin Nat100 blueMaybeN)
-> ColorThe type
Nat (ArgIn min Nat100 maybeN)is saying it wants:
an integer >= 0 Nat
in a range ArgIn
at least any minimum value min
at most 100 Nat100
which might be exact maybeNThey can prove it by
red = rgbPer100 nat100 nat0 nat0 -- 👍
nat0 : Nat (N Nat0 atLeast0 ...)
-- so it's also between 0 and 0/1/.../100
nat100 : Nat (N Nat100 (N100Plus orMore) ...)
-- so it's also between 100 and 100(/101/...)isUserIntANat : Int -> Maybe (Nat (Min Nat0))
isUserIntANat =
Nat.isIntAtLeast nat0grey float =
let
greyLevel =
float
* 100
|> round
|> Nat.intInRange nat0 nat100
in
rgbPer100 greyLevel greyLevel greyLevel
toDigit : Char -> Maybe IntYou might be able to do anything with this Int value, but you lost useful information.
toDigit : Char -> Maybe Digit
type alias Digit =
Nat (In Nat0 Nat9)The type of a value reflects how much you know.
In: between a minimum & maximum valueMin: at least a minimum valueN: exact value
intFactorial : Int -> Int
intFactorial x =
if x == 0 then
1
else
x * intFactorial (x - 1)This forms an infinite loop if we call intFactorial -1...
Let's disallow negative numbers here!
factorial : Nat (ArgIn min max maybeN) -> Nat (Min Nat1)Says: for every natural number n >= 0, n! >= 1.
factorial =
let
factorialBody x =
case x |> MinNat.isAtLeast nat1 { min = nat0 } of
Nat.Below _ ->
MinNat.value nat1
Nat.EqualOrGreater atLeast1 ->
-- atLeast1 is a Nat (Min Nat1)
Nat.mul atLeast1
(factorial
(atLeast1 |> MinNat.subN nat1)
-- so we can subtract 1
)
in
Nat.lowerMin nat0 >> factorialBody
factorial nat4 --> Nat 24→ There is no way to put a negative number in.
→ We have the extra promise, that every result is >= 1
We can do even better!
We know that !19 is already greater than the maximum safe Int 2^53 - 1.
safeFactorial : Nat (ArgIn min Nat18 maybeN) -> Nat (Min Nat1)
safeFactorial =
factorialNo extra work.
squares2To10 =
Nat.range nat2 nat10
-- more info than List.range 2 10
-- → every Nat is In Nat2 (Nat10Plus a)
|> List.map
(Nat.toPower nat2
-- we still know it's >= 2
)Instead of accepting only exact values
rgb : Nat (ArgN red (Is redTo100 To Nat100) x) -> --...accept values that are somewhere in a range.
rgb : Nat (ArgIn redMin Nat100 maybeN) -> --...maybeN says that it can be exact anyway. Or instead of
charFromCode : Nat (Min min) -> Charwhich you should also never do, allow Nat (In min ...) with any max & Nat (N ...) to fit in as well!
charFromCode : Nat (ArgIn min max maybeN) -> CharTake a look at elm-typesafe-array to see a lot of this in action!
You get to know that
Nat (In ...) is very useful as an indexNat.Bounds can describe amounts well