Natural numbers within a typed range.
somewhere within a minimum & maximum
↓ minimum ↓ maximum
⨯ [✓ ✓ ✓ ✓ ✓ ✓ ✓] ⨯ ⨯ ⨯...In minimum maximum difference_For arguments
percent : N (In min_ N100 difference_) -> Percent→ min ≤ N100
If you want a number where you just care about the minimum, leave the max as a type variable.
↓ minimum ↓ maximum or →
⨯ [✓ ✓ ✓ ✓ ✓ ✓ ✓...Any natural number:
N (In min_ max_ difference_)A number, at least 5:
N (In (Add5 minMinus5_) max_ difference_)max_ could be a specific maximum or no maximum at all
difference_ could contain extra information if the argument is a specific number
like n0, n1, ...
In minimum maximum {}A number somewhere within a minimum & maximum. We don't know it exactly, though
↓ minimum ↓ maximum
⨯ [✓ ✓ ✓ ✓ ✓ ✓ ✓] ⨯ ⨯ ⨯...Do not use it as an argument type.
A number between 3 and 5
N (In N3 (Add5 a_) {})Only stored / result types should be Min.
Sometimes, you simply cannot compute a maximum.
abs : Int -> N (In N0 ?? {})
↓
abs : Int -> N (Min N0)A number ≥ 5 for example:
N (Min N5)Every Min min is of type In min ....
Flag "The number's maximum limit is unknown".
type alias Min min =
In minimum NoMax {}is the definition of Min.
An example where this is useful using typesafe-array:
type Tree childCountMax element
= Tree
element
(Arr
(In N0 childCountMax {})
(Maybe (Tree childCountMax element))
)
type alias TreeBinary element =
Tree N2 element
type alias TreeMulti element =
Tree NoMax elementRemember: ↑ and other Min/NoMax are result/stored types, not argument types.
You can just use a variable Tree childCountMax_ element if you don't care about an enforced maximum.
Allow only a specific number.
Useful as a stored & argument type in combination with typesafe-arrays,
not with Ns.
byte : Arr (Exactly N8) Bit -> Byte→ A given Arr must have exactly 8 Bits.
type alias TicTacToeBoard =
Arr
(Exactly N3)
(Arr (Exactly N3) TicTacToField)→ A given Arr must have exactly 3 by 3 TicTacToeFields.
The absolute value of an Int → ≥ 0
-4 |> N.abs |> N.toInt --> 4
16 |> N.abs |> N.toInt --> 16Really only use this if you want the absolute value.
badLength =
List.length >> N.absmaybe, there's a solution that never even theoretically deals with unexpected values:
mostCorrectLength =
List.foldl
(\_ -> N.minAdd n1 >> N.minDown n0)
(n0 |> N.noMax)other times, though, like with Array.length, which isn't O(n),
you can escape with for example
arrayLength =
Array.length >> N.intAtLeast n0Generate a random N in a range.
N.randomIn ( n1, n10 )
--: Random.Generator (N (In N1 (Add10 a_) {}))Ns increasing from 0 to n
In the end, there are n numbers.
N.until n6
|> List.map (N.add n3)
--: List (N (In N3 (Add9 a_)))
|> List.map N.toInt
--> [ 3, 4, 5, 6, 7, 8, 9 ]
N.up atLeast7 |> List.map (N.add n3)
--: List (N (Min N10))typesafe-array even knows the length! Try it.
Ns increasing from 0 to n - 1.
In the end, there are n numbers.
@deprecated in favor of
untilIf you'd like to keep this → issue
N.up n7
|> List.map (N.add n3)
--: List (N (In N3 (Add9 a_)))
|> List.map N.toInt
--> [ 3, 4, 5, 6, 7, 8, 9 ]typesafe-array even knows the length! Try it.
If the package exposed every number 0 → 1000, tools can become unusably slow.
So only 0 → 16 are exposed, while larger numbers have to be generated locally.
Current method: generate them into a module exposing (n500, N500, Add500, ...) + import as N exposing (n500, ...)
In the future, elm-generate will allow auto-generating via elm-review.
nType for the exact natural number 2
Type for the exact natural number 3
Type for the exact natural number 4
Type for the exact natural number 5
Type for the exact natural number 6
Type for the exact natural number 7
Type for the exact natural number 8
Type for the exact natural number 9
Type for the exact natural number 10
Type for the exact natural number 11
Type for the exact natural number 12
Type for the exact natural number 13
Type for the exact natural number 14
Type for the exact natural number 15
Type for the exact natural number 16
n +Base type of N0, Add1 n following allowable-state:
Is the parameter possiblyOrNever set to Never like in
type alias Add1 n =
N0able n NeverN0 is impossible to construct.
Is the parameter possiblyOrNever set to Possibly like in
type alias N0 =
N0able Never PossiblyN0 is a possible value.
Type for the natural number 2 + some natural number n
Type for the natural number 3 + some natural number n
Type for the natural number 4 + some natural number n
Type for the natural number 5 + some natural number n
Type for the natural number 6 + some natural number n
Type for the natural number 7 + some natural number n
Type for the natural number 8 + some natural number n
Type for the natural number 9 + some natural number n
Type for the natural number 10 + some natural number n
Type for the natural number 11 + some natural number n
Type for the natural number 12 + some natural number n
Type for the natural number 13 + some natural number n
Type for the natural number 14 + some natural number n
Type for the natural number 15 + some natural number n
Type for the natural number 16 + some natural number n
The exact natural number 1
The exact natural number 2
The exact natural number 3
The exact natural number 4
The exact natural number 5
The exact natural number 6
The exact natural number 7
The exact natural number 8
The exact natural number 9
The exact natural number 10
The exact natural number 11
The exact natural number 12
The exact natural number 13
The exact natural number 14
The exact natural number 15
The exact natural number 16
A N (Min ...) from an Int; if the Int < minimum, minimum is returned.
0
|> N.intAtLeast n3
--: N (Min N3)
|> N.toInt
--> 3
9
|> N.intAtLeast n3
--: N (Min N3)
|> N.toInt
--> 9You can also use this if you know an Int must be at least minimum.
But avoid it if you can do better, like
goodLength =
List.foldl
(\_ -> N.minAdd n1 >> N.minDown n0)
(n0 |> N.noMax)If you want to handle the case < minimum yourself → intIsAtLeast
Create a N (In ...) by clamping an Int between a minimum & maximum.
Int < minimum, minimum is returnedInt > maximum, maximum is returnedIf you want to handle the cases < minimum & > maximum explicitly, use intIsIn.
0
|> N.intIn ( n3, n12 )
--: N (In N3 (Add12 a_) {})
|> N.toInt
--> 3
99
|> N.intIn ( n3, n12 )
--: N (In N3 (Add12 a_) {})
|> N.toInt
--> 12
9
|> N.intIn ( n3, n12 )
--: N (In N3 (Add12 a_) {})
|> N.toInt
--> 9
toDigit : Char -> Maybe (N (In N0 (Add9 atLeast_) {}))
toDigit char =
((char |> Char.toCode) - ('0' |> Char.toCode))
|> N.intIsIn ( n0, n9 )
|> Result.toMaybeCompared to a range from a lower to an upper bound, is the Int in range, BelowOrAbove?
inputIntJudge : Int -> Result String (N (In N1 (Add10 a_) {}))
inputIntJudge int =
case int |> N.intIsIn ( n1, n10 ) of
Ok inRange ->
inRange |> N.maxOpen n10 |> Ok
Err (N.Below _) ->
Err "must be ≥ 1"
Err (N.Above _) ->
Err "must be ≤ 100"
0 |> inputIntJudge
--> Err "must be ≥ 1"If the Int ≥ a given minimum,
return Ok with the N (Min minimum),
else Err with the input Int.
4 |> N.intIsAtLeast n5
--: Result Int (N (Min N5))
--> Err 4
1234 |> N.intIsAtLeast n5 |> Result.map N.toInt
--> Ok 1234Return the given number if the N is less.
between5And9 |> N.atLeast n10
--: N (In N10 (Add10 a_) {})
n15 |> N.atLeast n10 |> N.toInt
--> 15
n5AtLeast |> N.atLeast (n10 |> noMax)
--: N (Min N10)(The type doesn't forbid that the lower limit you're comparing against is below the current minimum.)
Cap the N to at most a number.
between5And15
|> N.atMost n10
--: N (In N5 (Add10 a_) {})
atLeast5 |> N.atMost n10
--: N (In N5 (Add10 a_) {})(The type doesn't forbid that the upper limit you're comparing against is above the current maximum.)
Is the N equal to, BelowOrAbove a given number?
giveAPresent { age } =
case age |> N.is n18 of
Err (N.Below younger) ->
toy { age = younger }
Err (N.Above older) ->
book { age = older }
Ok _ ->
bigPresent
toy : { age : N (In min_ N17 difference_) } -> Toy
book :
{ age : N (In (N19 minMinus19_) max_ difference_) }
-> Book(The type doesn't forbid that the number you're comparing against
is below the current minimum or above the current maximum.
→ Err or Ok values don't necessarily follow min <= max for N (In min max ...)
Luckily that's not a problem, since the values won't be produced anyway.)
Compared to a range from a lower to an upper bound,
is the N in range or BelowOrAbove?
isIn3To10 : N (In min_ (Add10 maxMinus10_) d_) -> Maybe (N (In N3 (Add10 a_) {}))
isIn3To10 =
N.isIn ( n3, n10 )
>> Result.toMaybe
n9 |> isIn3To10 |> Maybe.map N.toInt
--> Just 9
n12 |> isIn3To10
--> Nothing(The type doesn't forbid that the limits you're comparing against
are below the current minimum, above the current maximum or in the wrong order.
→ Err or Ok values don't necessarily follow min <= max for N (In min max ...)
Luckily that's not a problem, since the values won't be produced anyway.)
Here's some example-cases:
hi < lo < min < max → Below | Abovehi < min < lo < max → Below | Abovehi < min < max < lo → Below | Abovelo < hi < min < max → Abovemax < hi < min < lo → Belowmin < hi < lo < max → Below | Abovelo < min < hi < max → Ok | Abovemax < min < hi < lo → Belowmax < lo < hi < min → Below | Ok | Above ← the default caselo < min < max < hi → Okmin < lo < max < hi → Ok | Belowmin < max < lo < hi → Below.
Is the N below than or at least as big as a given number?
vote :
{ age : N (In (Add18 minMinus18_) max_ difference_) }
-> Vote
tryToVote { age } =
case age |> N.isAtLeast n18 of
Err _ ->
--😓
Nothing
Ok oldEnough ->
vote { age = oldEnough } |> Just
factorial : N (In min_ max_ difference_) -> N (Min N1)
factorial =
factorialBody
factorialBody : N (In min_ max_ difference_) -> N (Min N1)
factorialBody x =
case x |> N.isAtLeast n1 of
Err _ ->
n1 |> N.noMax
Ok atLeast1 ->
factorial (atLeast1 |> N.minSub n1)
|> N.mul atLeast1(The type doesn't forbid that the lower limit you're comparing against
is below the current minimum or above the current maximum.
→ Err or Ok values don't necessarily follow min <= max for N (In min max ...)
Luckily that's not a problem, since the values won't be produced anyway.)
Is the N at most (Ok) or greater than (Err) a given number?
goToBelow18Party : { age : N (In min_ N17 difference_) } -> Snack
tryToGoToBelow18Party { age } =
case age |> N.isAtMost n17 of
Ok below18 ->
goToBelow18Party { age = below18 } |> Just
Err _ ->
Nothing(The type doesn't forbid that the upper limit you're comparing against
is below the current minimum or above the current maximum.
→ Err or Ok values don't necessarily follow min <= max for N (In min max ...)
Luckily that's not a problem, since the values won't be produced anyway.)
Add a given specific N (In ... (Is ...)).
between70And100 |> N.add n7
--: N (In N77 (Add107 a_) {})Use addIn if you want to add an N that isn't a N (In ... (Is ...)).
Add an N that can be in a range.
The tuple argument should contain
between3And10 |> N.addIn ( n1, n12 ) between1And12
--: N (In N4 (N22Plus a_) {})Subtract a given specific N (In ... (Is ...)).
between7And10 |> N.sub n7
--: N (In N0 (Add3 a_) {})Use subIn if you want to subtract an N that isn't a N (In ... (Is ...)).
Subtract an N that can be in a range.
The tuple argument should contain
between6And12 |> N.subIn ( n1, n5 ) between1And5
--: N (In N1 (Add5 a_) {})N Raised to a given power p ≥ 1
→ x ^ p ≥ x
atLeast5 |> N.toPower n2
--: N (Min N5)
atLeast2 |> N.toPower n5
--: N (Min N2)The remainder after dividing by a N d ≥ 1.
We know x % d ≤ d - 1
atMost7 |> N.remainderBy n3
--: N (In N0 (Add3 a_) {})Multiply by N n ≥ 1.
we know that if n ≥ 1, x * n ≥ x.
atLeast5 |> N.mul n2
--: N (Min N5)
atLeast2 |> N.mul n5
--: N (Min N2)Divide (//) by an N d ≥ 1.
/ 0 is impossiblex / d is at most xatMost7
|> N.div n3
--: N (In N0 (Add7 a_) {})
|> N.toInt
--> 2From an N with an unknown maximum constraint,
subtract a specific number
atLeast7 |> N.minSub n2
--: N (Min N5)Use minSubAtMost if you want to subtract an N that can be in a range.
To the N without a known maximum-constraint,
add a specific N (In ... (Is ...)) value.
atLeast70 |> N.add n7
--: N (Min N77)Use addAtLeast if you want to add an N that can be in a range.
From an N without an unknown maximum constraint,
subtract a number in a range.
The first argument is the maximum of the subtracted number.
atLeast6 |> N.minSubAtMost n5 between0And5
--: N (Min N1)Drop the range constraints
to feed another library with its Int representation.
Drop the range constraints
to feed another library with its Float representation.
Set the minimum lower.
[ atLeast3, atLeast4 ]Elm complains:
But all the previous elements in the list are:
N (Min N3)
[ atLeast3
, atLeast4 |> N.minDown n3
]Convert it to a N (In min max {}).
n4 |> N.noDiff
--: N (In N4 (Add4 a_) {})Example
[ in3To10, n3 ]all the previous elements in the list are:
N (In N3 N10 {})
[ in3To10
, n3 |> N.noDiff
]Convert a N (In min ...) to a N (Min min).
between3And10 |> N.noMax
--: N (Min N4)Use it to unify different types of number minimum constraints like
[ atLeast1, between1And10 ]Elm complains:
But all the previous elements in the list are:
N (Min N1)
[ atLeast1
, between1And10 |> N.noMax
]Make it fit into functions with require a higher maximum.
You should type arguments and stored types as broad as possible.
onlyAtMost18 : N (In min_ N18 difference_) -> ...
onlyAtMost18 between3And8 -- worksBut once you implement onlyAtMost18, you might use the n in onlyAtMost19:
onlyAtMost18 n =
-- onlyAtMost19 n → error
onlyAtMost19 (n |> N.maxOpen n18)Have a specific maximum in mind? → maxOpen
Want to increase the upper bound by a fixed amount? ↓
maxUp4 : N (In min max difference_) -> N (In min (Add4 max) {})
maxUp4 =
N.maxUp n4When is this useful? Very rarely. This is how up reverse could be implemented
down :
N (In min (Add1 maxMinus1) difference_)
-> List (N (In N0 maxMinus1 {}))
down length =
case length |> isAtLeast n1 of
Err _ ->
[]
Ok lengthAtLeast1 ->
let
lengthMinus1 =
lengthAtLeast1 |> sub n1
in
lengthMinus1
:: (lengthMinus1
-- → In N0 maxMinus1
|> maxUp n1
|> downRecursive
)(to be fair: here, you could also use lengthAtLeast1 |> subMin n1 instead of lengthMinus1 |> maxUp n1)
Anything that can't be expressed with the available operations? → issue/PR
Useful for fancy things – building structures like typesafe-array
While the internally stored Int isn't directly guaranteed to be in bounds by elm,
minimum, maximum, differences
must be built as actual values checked by the compiler.
Associated differences.
Diff low To high: an exact number as the difference high - low.
In
N5
(Add5 a_)
(Is
(Diff myAge To sistersAge)
(Diff mothersAge To fathersAge)
)myAge + 5 = sistersAgemothersAge + 5 = fathersAgeThe N (In ... (Is ...)) plus another N (In ... (Is ...)). Give the added value twice as a tuple.
n6 |> N.diffAdd ( n5, n5 )
--→ n11
--: N
--: (In
--: N11
--: (Add11 a_)
--: (Is
--: (Diff x0 To (Add11 x0))
--: (Diff x1 To (Add11 x1))
--: )
--: )This is only rarely useful, as for example
isInRangeXToXPlus10 x =
isIn ( x, x |> N.diffAdd ( n10, n10 ) )would force x to be of type N (In ... (Is ...)). Instead,
isInXToXPlus10 x =
isIn ( x, x |> N.add n10 )The N (In ... (Is ...) minus another N (In ... (Is ...)). Give the subtracted value twice as a tuple.
n6 |> N.diffSub ( n5, n5 )
--→ n1
--: N
--: (In
--: N1
--: (Add1 a_)
--: (Is
--: (Diff x0 To (Add1 x0))
--: (Diff x1 To (Add1 x1))
--: )
--: )This is only rarely useful, as for example
isInXMinus10ToX x =
isIn ( x |> N.diffSub ( n10, n10 ), x )would force x to be of type N (In ... (Is ...)).
Instead,
isInXMinus10ToX x =
isIn ( x |> N.sub n10, x )Constructor for NoMax
The smallest allowed number promised by the range type.
The number representation as a difference promised by its type Is (Diff low To high) ....
The number representation as a difference promised by its type Is ... (Diff low To high).
The number representation as all differences promised by its type Is (Diff low0 To high0) (Diff low1 To high1).
For the individual differences: difference0, difference1
Switch difference0, difference1 of the specific type N.
addInSwapped :
( N (In addedMin addedMinAtLeast_ (Is (Diff min To sumMin) addedMinDiff1_))
, N (In addedMax addedMaxAtLeast_ (Is (Diff max To sumMax) addedMaxDiff1_))
)
-> N (In addedMin addedMax addedDifference_)
->
(N (In min max difference_)
-> N (In sumMin sumMax {})
)
addInSwapped ( addedAtLeast, addedAtMost ) toAdd =
N.addIn ( addedAtLeast, addedAtMost |> N.differencesSwap ) toAdd
add toAdd =
addInSwapped ( toAdd, toAdd ) toAddTo the number, add a specific other one by supplying one of its differences
promised by its type Is (Diff low0 To high0) (Diff low1 To high1)
To the number, subtract a specific other one by supplying one of its differences
promised by its type Is (Diff low0 To high0) (Diff low1 To high1)
Chain the difference further to a higher number.
Chain the difference back to a lower number.
A bounded natural number
>= 0argument type
In (Add4 minMinus4_) N15says:4|5|6|...15is forbiddenn0,n1, ... which carrydifference_s are allowedresult type
In N2 (Add12 a_) {}says:4|5|6|...15is forbidden{}: no differences are attached. Only specific numbers liken0,n1, ... carry differencessstored type
what to put in declared types like
ModelThey are like result types but without type variables.
specific number
Numbers
n0,n1, ... supplied by this library.The type
Is(Diff x0 To nPlusX0) ...enables adding, subtractingN<x>types. Consider the type an implementation detail. You can come back to understand them later.