indique/elm-bounded-nat/8.0.0

NOTE: the latest version is 13.0.0

Nat.In

A natural number within a minimum & maximum.

add information

clamp

Nat (In min firstMax)
-> Nat (In firstMax max)
-> Int
-> Nat (In min max)

A Nat (In ...) from an Int, clamped between a minimum & maximum.

  • if the Int < minimum, minimum is returned
  • if the Int > maximum, maximum is returned
clampBetween3And12 =
    Nat.In.intInRange (nat3 |> Nat.N.toIn) (nat12 |> Nat.N.toIn)

9 |> clampBetween3And12 --> Nat 9

0 |> clampBetween3And12 --> Nat 3

99 |> clampBetween3And12 --> Nat 12

If you want to handle the cases < minimum & > maximum explicitly, use isIntInRange.

atLeast : Nat (In newMin max) -> Nat (In min max) -> Nat (In newMin max)

If the Nat (In ...) is less than a number, return that number instead.

nat5 |> Nat.N.toIn
    |> Nat.In.atLeast nat10
--> Nat 10


nat15 |> Nat.N.toIn
    |> Nat.In.atLeast (nat10 |> Nat.N.toIn)
--> Nat 15
Nat (N newMax (Is a To newMaxPlusA) x)
-> { min : Nat (N min (Is newRange To newMax) y) }
-> Nat (In min oldMax)
-> Nat (In min newMaxPlusA)

Cap the Nat (In ...) to at most a number.

nat5 |> Nat.N.toIn
    |> Nat.In.atMost nat10 { min = nat5 }
--> Nat.In 5


nat15 |> Nat.N.toIn
    |> Nat.In.lowerMin (nat5 |> Nat.N.toIn)
    |> Nat.In.atMost nat10 { min = nat5 }
--> Nat 10

min ensures that that number is at least the minimum.

compare

is :
Nat
(N
tried
(Is (Nat1Plus greaterRange) To max)
(Is a To (Nat1Plus triedPlusAMinus1))
)
-> { min : Nat (N min (Is (Nat1Plus lessRange) To tried) x) }
-> { equal : () -> result
, less : Nat (In min triedPlusAMinus1) -> result
, greater : Nat (In (Nat2Plus triedMinus1) max) -> result
}
-> Nat (In min max)
-> result

Compare the Nat (In ...) to a Nat (N ...). Is it greater, less or equal?

min ensures that the Nat (N ...) is bigger than the minimum.

present =
    Nat.In.lowerMin (nat0 |> Nat.N.toIn)
        >> Nat.In.is nat18
            { min = nat0 }
            { less = \age -> appropriateToy { age = age }
            , greater = \age -> appropriateExperience { age = age }
            , equal = \() -> bigPresent
            }


appropriateToy : { age : Nat (In Nat0 Nat17) } -> Toy


appropriateExperience : { age : Nat (In Nat19 max) } -> Experience
{ first :
Nat
(N
first
(Is range To last)
(Is a To (Nat1Plus firstMinus1PlusA))
)
, last :
Nat
(N
last
(Is (Nat1Plus greaterRange) To max)
(Is a To lastPlusA)
)
}
-> { min : Nat (N min (Is (Nat1Plus lessRange) To first) x) }
-> { inRange : Nat (In first lastPlusA) -> result
, less : Nat (In min firstMinus1PlusA) -> result
, greater : Nat (In (Nat1Plus last) max) -> result
}
-> Nat (In min max)
-> result

Compared to a range first to last, is the Nat (In ...)

  • inRange

  • greater than the last or

  • less than the first?

justIfBetween3And10 =
    Nat.In.lowerMin (nat0 |> Nat.N.toIn)
        >> Nat.In.isInRange { first = nat3, last = nat10 }
            { min = nat0 }
            { less = \_ -> Nothing
            , greater = \_ -> Nothing
            , inRange = Just
            }

justIfBetween3And10 (nat9 |> Nat.N.toIn)
--> Just (Nat 9)

justIfBetween3And10 (nat123 |> Nat.N.toIn)
--> Nothing
{ first : Nat (In minFirst last)
, last : Nat (In last lastPlusA)
}
-> { less : () -> result
, greater : Nat (Min (Nat1Plus last)) -> result
, inRange : Nat (In minFirst lastPlusA) -> result
}
-> Int
-> result

An Int compared to a range from first to last.

rejectOrAcceptUserInt =
    isIntInRange (nat1 |> Nat.N.toIn) (nat100 |> Nat.N.toIn)
        { less = Err "must be >= 1"
        , greater = \_-> Err "must be <= 100"
        , inRange = Ok
        }


rejectOrAcceptUserInt (Nat.N.toIn nat0)
--> Err "must be >= 1"
Nat
(N
tried
(Is a To (Nat1Plus triedMinus1PlusA))
(Is atLeastRange To max)
)
-> { min : Nat (N min (Is (Nat1Plus lessRange) To tried) x) }
-> { less : Nat (In min triedMinus1PlusA) -> result
, equalOrGreater : Nat (In tried max) -> result
}
-> Nat (In min max)
-> result

Is the Nat (In ...)

  • equalOrGreater than a Nat or

  • less?

vote : { age : Nat (In (Nat18Plus orOlder) max) } -> Vote

tryToVote =
    Nat.In.lowerMin (nat0 |> Nat.N.toIn)
        >> Nat.In.isAtLeast nat18
            { min = nat0 }
            { less = Nothing --😓
            , equalOrGreater = \age -> Just (vote { age = age })
            }
Nat
(N
tried
(Is a To triedPlusA)
(Is (Nat1Plus greaterRange) To max)
)
-> { min : Nat (N min (Is atMostRange To tried) x) }
-> { equalOrLess : Nat (In min triedPlusA) -> result
, greater : Nat (In tried max) -> result
}
-> Nat (In min max)
-> result

Is the Nat (In ...)

  • equalOrLess than a Nat or

  • greater?

goToU18Party : { age : Nat (In min Nat17) } -> List Snack

tryToGoToU18Party =
    Nat.In.lowerMin (nat0 |> Nat.N.toIn)
        >> Nat.In.isAtMost nat17
            { min = nat0 }
            { equalOrLess = \age -> Just (goToU18Party { age 0 age })
            , greater = Nothing
            }

modify

Nat (N added (Is min To sumMin) (Is max To sumMax))
-> Nat (In min max)
-> Nat (In sumMin sumMax)

Add a fixed Nat (N ...) value.

nat70 |> Nat.N.toIn
    |> Nat.In.addN nat7
--> is of type Nat (In Nat77 (Nat77Plus a))
Nat
(N
subtracted
(Is differenceMin To min)
(Is differenceMax To max)
)
-> Nat (In min max)
-> Nat (In differenceMin differenceMax)

Subtract a fixed Nat value.

nat7 |> Nat.N.toIn
    |> Nat.In.subN nat7
--> is of type Nat (In Nat0 a)
add :
Nat (In addedMin addedMax)
-> Nat (N addedMin (Is min To sumMin) x)
-> Nat (N addedMax (Is max To sumMax) y)
-> Nat (In min max)
-> Nat (In sumMin sumMax)

Add a Nat (In ...).

Nat.N.toIn nat3
    |> Nat.In.add between1And12 nat1 nat12
--> of type Nat (In Nat4 (Nat15Plus a))
sub :
Nat (In subtractedMin subtractedMax)
-> Nat (N subtractedMin (Is differenceMax To max) x)
-> Nat (N subtractedMax (Is differenceMin To min) y)
-> Nat (In min max)
-> Nat (In differenceMin differenceMax)

Subtract a Nat (In ...).

The 2 following arguments are

  • the minimum subtracted value
  • the maximum subtracted value

If you don't have both at hand, use subLossy.

nat6 |> Nat.N.toIn
    |> Nat.In.sub between1And5 nat1 nat5
--> is of type Nat (In Nat1 (Nat5Plus a))
div :
Nat (Min (Nat1Plus dMinMinus1))
-> Nat (In min max)
-> Nat (In Nat0 max)

Divide (//) by a Nat (Min ...). div 0 is impossible.

Nat.N.toIn nat7 |> Nat.In.div (nat3 |> Nat.N.toMin)
--> Nat 2 of type Nat (In Nat0 (Nat7Plus a))
Nat (Min (Nat1Plus dMinMinus1))
-> Nat (In min max)
-> Nat (In Nat0 max)

The remainder after division. remainderBy 0 is impossible.

Nat.N.toIn nat7 |> Nat.In.remainderBy (nat3 |> Nat.N.toMin)
--> Nat 1 of type Nat (In Nat0 (Nat7Plus a))
subLossy : Nat (In subtractedMin min) -> Nat (In min max) -> Nat (In Nat0 max)

Subtract a Nat (In ..) safely, but without calculating the new bounds.

nat6 |> Nat.N.toIn
    |> Nat.In.subLossy between1And5
--> is of type Nat (In Nat0 (Nat6Plus a))

If you have both

  • the minimum subtracted value
  • the maximum subtracted value

at hand, use sub.

If you want other operations like multiply, use dropMax.

drop information

lowerMin : Nat (In lowerMin min) -> Nat (In min max) -> Nat (In lowerMin max)

Set the minimum lower.

[ Nat.N.toIn nat3, Nat.N.toIn nat4 ]

Elm complains:

But all the previous elements in the list are: Nat (In Nat3 ...)

[ Nat.N.toIn nat3
, Nat.N.toIn nat4 |> Nat.In.lowerMin (nat3 |> Nat.N.toIn)
]
Nat (N max (Is a To maxPlusA) x)
-> Nat (In min max)
-> Nat (In min maxPlusA)

Make it fit into functions with higher maximum.

You should design type annotations as general as possible.

onlyAtMost18 : Nat (In min Nat18)


onlyAtMost18 between3And8 --fine

But once you implement onlyAtMost18, you might use the value in onlyAtMost19.

onlyAtMost18 value =
    -- onlyAtMost19 value --error :(
    onlyAtMost19
        (value |> Nat.In.maxIs nat18 {- works :) -})

lowerMin is also handy in those situations.

dropMax : Nat (In min max) -> Nat (Min min)

Convert a Nat (In ...) to a Nat (Min ...).

squareAInNat =
    Nat.In.dropMax
        >> Nat.Min.toPower (nat2 |> Nat.N.toMin)

more

Nat (In firstMin lastMin)
-> Nat (In lastMin lastMax)
-> Generator (Nat (In firstMin lastMax))

Generate a random Nat (In ...) in a range.

Nat (In firstMin lastMin)
-> Nat (In lastMin lastMax)
-> List (Nat (In firstMin lastMax))

Nat (In ...)s from a first to a last value.

from3To10 =
    Nat.In.range (nat3 |> Nat.N.toIn) (nat10 |> Nat.N.toIn)

The resulting List is never empty.