Type-safe natural numbers (>= 0) can ensure that a Nat is in a given range at compile-time.
toHexChar :
Nat (ArgIn anyMinimum_ Nat15 _)
-> 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, ArgIn)
import InNat
import MinNat
import Nats exposing (..)
-- nat0-160, Nat0-160 & -Plus
import Typedpercent : Float -> Lengthis common.
Float between 0 & 1 is wantedwith bounded-nat
percent :
Nat (ArgIn min_ Nat100 ifN_)
-> LengthThe type
Nat (ArgIn min_ Nat100 ifN_)is saying it wants:
an integer >= 0: Nat
ArgInmin_Nat100ifN_Use can prove this by
using exact values
twoThirds = percent nat67
red = rgbPercent nat100 nat0 nat0 -- 👍handling the possibility that a number isn't in the expected range
toPositive : Int -> Maybe (Nat (Min Nat1))
toPositive =
Nat.isIntAtLeast nat1clamping
floatPercent float =
Nat.intInRange nat0 nat100
(float * 100 |> round)There are more ways, but you get the idea 🙂
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.
InMinN
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_ ifN_) -> Nat (Min Nat1)
factorial =
factorialBodySays: for every natural number n >= 0, n! >= 1.
factorialBody : -- as in factorial
factorialBody =
case x |> MinNat.isAtLeast nat1 { lowest = nat0 } of
Nat.Below _ ->
Nat.toMin nat1
Nat.EqualOrGreater atLeast1 ->
-- atLeast1 is a Nat (Min Nat1)
Nat.mul atLeast1
(factorial
(atLeast1 |> MinNat.sub nat1)
-- so we can subtract 1
)
factorial nat4 --> Nat 24→ There is no way to put a negative number in.
→ We have the extra promise, that 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 : Nat (ArgIn min_ Nat18 ifN_) -> Nat (Min Nat1)
safeFactorial =
factorialNo extra work.
keep as much type information as possible and drop it only where you need to.
keep your function annotations as general as possible
Instead of accepting only exact values
percent :
Nat (N p_ atLeast_ (Is to100_ To Nat100) is_)
-> Lengthaccept values that are somewhere in a range.
percent : Nat (ArgIn min_ Nat100 ifN_) -> LengthifN_ 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_ ifN_) -> CharTake a look at typesafe-array to see a lot of this in action!
You get to know that
Nat (ArgIn ...) is very useful as an indexIn, Min, Only can also describe the array length