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.
factorialHelp =
MinNat.isAtLeast nat1
{ min = nat0 } -- the minimum of the x
{ less =
-- x < 1 ? → then 1
\_ -> nat1 |> MinNat.value
, equalOrGreater =
\atLeast1 ->
-- a Nat (Min Nat1)
atLeast1
|> Nat.mul
(factorial
(atLeast1 |> MinNat.subN nat1)
-- we can subtract 1 👍
)
}As the minimum is allowed to be anything >= 0:
factorial =
Nat.lowerMin nat0
>> factorialHelp→ 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 =
-- every Nat is In Nat2 (Nat10Plus a)
Nat.range nat2 nat10
|> List.map
(Nat.toPower nat2
-- we can't compute the exact minimum & maximum
-- but we know it's at least Nat2
)Instead of accepting only exact values
rgb : Nat (N red (Is redTo100 To Nat100) x) -> --...accept values that are somewhere in a range.
rgb : Nat (In 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