Natural number within a typed range
somewhere within a minimum & maximum
↓ minimum ↓ maximum
⨯ [✓ ✓ ✓ ✓ ✓ ✓ ✓] ⨯ ⨯ ⨯...-- 3 ≤ n ≤ 5
N (In (On (Add3 minFrom3_)) (Up maxTo5_ To N5))
-- 0 ≤ n ≤ 100
percent : N (In min_ (Up maxTo100_ To N100)) -> PercentFor every constructable value, the minimum is smaller than the maximum
(that's why In is opaque)
If you want a number where you just care about the minimum, leave max as a type variable
↓ minimum ↓ maximum or →
⨯ [✓ ✓ ✓ ✓ ✓ ✓ ✓...
-- any natural number
N (In min_ max_)A number, at least 5:
N (In (On (Add5 minFrom_)) max_)→ max_ could be a specific maximum or no maximum at all
n3 : N (In (Up3 minX_) (Up3 maxX_))
between3And6 : N (In (Up3 minX_) (Up6 maxX_))
between3And6 |> N.add n3
--: N (In (Up6 minX_) (Up9 maxX_))An example where this is useful using typesafe-array:
type Tree branchingFactor element
= Tree
element
(ArraySized
branchingFactor
(Maybe (Tree branchingFactor element))
)
type alias TreeBinaryFull element =
Tree (Exactly (On N2)) elementRemember: ↑ and other ... (On...)
are result/stored types, not argument types
Do not use == on 2 values storing a range.
It will lead to elm crashing because differences are stored as functions.
Instead,
Only stored / result types should use Min
↓ minimum ↓ or →
⨯ [✓ ✓ ✓ ✓ ✓ ✓ ✓...Sometimes, you simply cannot compute a maximum
intToAbsolute : Int -> N (In (Up0 x_) ??)
↓
intToAbsolute : Int -> N (Min (Up0 x_))
-- n ≥ 5
atLeast5 : N (Min (Up5 x_))
atLeast5 |> N.addMin n3
--: N (Min (Up8 x_))
n3 |> N.addMin atLeast5
--: N (Min (Up8 x_))Every Min min is of type In min ...,
so using a type variable for the maximum on arguments is highly encouraged
when no maximum constraint should be enforced
-- any natural number
N (In min_ max_)
-- number, at least 5
N (In (On (Add5 minFrom5_)) max_)An example where this is useful using typesafe-array:
type Tree branchingFactor element
= Tree
element
(ArraySized
branchingFactor
(Maybe (Tree branchingFactor element))
)
type alias TreeMulti element =
Tree (Min (On N1)) elementRemember: ↑ and other ... (On...)
are result/stored types, not argument types
You can just use Min ( On ...) when you don't have disadvantages storing functions.
Allow only a specific given represented number
result type:
Exactly (On N3)Use Exactly ... without On
for storing in a type without internal functions
Exactly N3This is pretty useless in combination with N
-- argument type
numberToInt : N (Exactly n) -> n -> Int
numberToInt wellThisWasPointless _ =
wellThisWasPointless |> N.toIntIt is useful as a stored & argument type
in combination with typesafe-array,
byte : ArraySized (Exactly (On N8)) Bit -> Byte→ A given ArraySized must have exactly 8 Bits
type alias TicTacToeBoard =
ArraySized
(Exactly (On N3))
(ArraySized (Exactly (On N3)) TicTacToeField)→ A given ArraySized must have exactly 3 by 3 TicTacToeFields
The fixed difference from 0 to a given number n
You'll find this either to require a certain minimum
entryOnlyForKids : N (In (On (Add6 minFrom6_)) (Up maxTo14_ To N14)) -> ()
entryOnlyForKids _ =
()Or in a stored type, which looks like a result type
where every Up<n> x is instead On N<n>
Do not use == on 2 numbers in a On range.
It will lead to elm crashing because differences are stored as functions internally.
Instead, compare in your code or convert to a value for other code:
Not storing functions etc. in app state, events, ... enables
==, for example== call → crashCalling == on a value will yield the correct result instead of crashing
On is defined as a difference from 0, so this independent type needed to be created
You can just use On when you don't have disadvantages storing functions
Up low To high: a specific number represented as the difference high - low
Down high To low: a specific number represented as the difference high - low
Generate a random N in a range
N.inRandom ( n1, n10 )
--: Random.Generator
--: (N (In (Up1 minX_) (Up10 maxX_)))Fuzzer for an N in a given range.
In contrast to N.inFuzz,
all Ns are equally as likely, even for larger ranges
import Fuzz
N.inFuzzUniform ( n2, n2 |> N.toPower n13 )
|> Fuzz.map N.toInt
|> Fuzz.examples 3
--> [ 2244, 4842, 3566 ]
N.inFuzz ( n2, n2 |> N.toPower n13 )
|> Fuzz.map N.toInt
|> Fuzz.examples 3
--> [ 26, 82, 17 ]ArraySized.upTo
to create increasing Ns.
ArraySized will even know the final length in its type!
Wanna try?
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 N.Local exposing (n<n>, N<n>, Add<n>, Up<n>, ...) +
import N.Local as N exposing (n<n>, N<n>, Add<n>, Up<n>, ...)
In the future, elm-generate will allow auto-generating via elm-review
type n ⏭ skip to last
The natural number 0
The natural number 1
The natural number 2
The natural number 3
The natural number 4
The natural number 5
The natural number 6
The natural number 7
The natural number 8
The natural number 9
The natural number 10
The natural number 11
The natural number 12
The natural number 13
The natural number 14
The natural number 15
The natural number 16
type n + ⏭ skip to last
The natural number 1 + a given n
The natural number 2 + a given n
The natural number 3 + a given n
The natural number 4 + a given n
The natural number 5 + a given n
The natural number 6 + a given n
The natural number 7 + a given n
The natural number 8 + a given n
The natural number 9 + a given n
The natural number 10 + a given n
The natural number 11 + a given n
The natural number 12 + a given n
The natural number 13 + a given n
The natural number 14 + a given n
The natural number 15 + a given n
The natural number 16 + a given n
type Up x To (n + x) ⏭ skip to last
3 as the difference Up x To (Add3 x)
4 as the difference Up x To (Add4 x)
5 as the difference Up x To (Add5 x)
6 as the difference Up x To (Add6 x)
7 as the difference Up x To (Add7 x)
8 as the difference Up x To (Add8 x)
9 as the difference Up x To (Add9 x)
10 as the difference Up x To (Add10 x)
11 as the difference Up x To (Add11 x)
12 as the difference Up x To (Add12 x)
13 as the difference Up x To (Add13 x)
14 as the difference Up x To (Add14 x)
15 as the difference Up x To (Add15 x)
16 as the difference Up x To (Add16 x)
exact ⏭ skip to last
IntThe Int's distance from 0.
This "absolute value" is always ≥ 0
-4
|> N.intToAbsolute
--: N (Min (Up0 x_))
|> N.toInt
--> 4
16 |> N.intToAbsolute |> N.toInt
--> 16Really only use this if you want the absolute value
badLength =
List.length >> N.intToAbsolutemaybe, there's a solution that never even theoretically deals with unexpected values:
mostCorrectLength =
List.foldl
(\_ -> N.addMin n1 >> N.minSubtract n1)
(n0 |> N.maxToInfinity)other times, though, like with Array.length, which isn't O(n),
you can escape with for example
arrayLength =
Array.length >> N.intToAtLeast n0Perform modular arithmetic by an N d ≥ 1.
That means x % d ≤ d - 1
7
|> N.intModBy n3
--: N (In (Up0 minX_) (Up2 maxX_))
|> N.toInt
--> 1Works in the typical mathematical way for a negative Int
-7
|> N.intModBy n3
--: N (In (Up0 minX_) (Up2 maxX_))
|> N.toInt
--> 2Already have an N? → remainderBy
A N (Min ...) from an Int;
if the Int < minimum, minimum is returned
0
|> N.intToAtLeast n3
--: N (Min (Up3 x_))
|> N.toInt
--> 3
9
|> N.intToAtLeast n3
--: N (Min (Up3 x_))
|> N.toInt
--> 9You can also use this as an escape hatch
if you know an Int must be at least minimum.
But avoid it if you can do better, like
goodLength =
List.foldl
(\_ -> N.addMin n1 >> N.minSubtract n1)
(n0 |> N.maxToInfinity)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.intToIn ( n3, n12 )
--: N (In (Up3 minX_) (Up12 minX_))
|> N.toInt
--> 3
99
|> N.intToIn ( n3, n12 )
|> N.toInt
--> 12
9
|> N.intToIn ( n3, n12 )
|> N.toInt
--> 9
toDigit : Char -> Maybe (N (In (Up0 minX_) (Up9 maxX_)))
toDigit char =
((char |> Char.toCode) - ('0' |> Char.toCode))
|> N.intIsIn ( n0, n9 )
|> Result.toMaybeInt compareIf 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 (Up5 x_)))
--> Err 4
1234 |> N.intIsAtLeast n5 |> Result.map N.toInt
--> Ok 1234Compared to a range from a lower to an upper bound, is the Int in range, BelowOrAbove?
inputIntParse : Int -> Result String (N (In (Up1 minX_) (Up10 maxX_)))
inputIntParse =
N.intIsIn ( n1, n10 )
>> Result.mapError
(\outOfRange ->
case outOfRange of
N.Below _ ->
"≤ 0"
N.Above _ ->
"≥ 11"
)
0 |> inputIntParse
--> Err "≤ 0"To the N without a known maximum-constraint,
add a number that (only) has information on how to add the minima
atLeast70 |> N.addMin n7
--: N (Min (Up77 x_))
n7 |> N.addMin atLeast70
--: N (Min (Up77 x_))Use add if both maxima are known differences as well
If the added minimum is On, supply the N.minTo manually
to re-enable adding both minimum types!
atLeast5 |> N.addMin (atLeastOn2 |> N.minTo n2)
--: N (Min (Up7 x_))From the N in a range subtract another N in a range
n6
|> N.subtract n5
--: N (In (On N1) (Up1 x_))
|> N.inToNumber
--> n1 |> N.inToNumberOne of the Ns has no maximum constraint? → N.subtractMin
From an N with an unknown maximum constraint,
subtract a specific number
atLeast7 |> N.subtractMin n2
--: N (Min (On N5))
atLeast6 |> N.subtractMin between0And5
--: N (Min (On N1))
between6And12 |> N.subtractMin between1And5
--: N (In (On min_) (Up12 maxX_))Use N.subtract if you want to subtract an N in a range
N Raised to a given power p ≥ 1
→ x ^ p ≥ x
atLeast5 |> N.toPower n2
--: N (Min (Up5 x_))
atLeast2 |> N.toPower n5
--: N (Min (Up2 x_))The remainder after dividing by an N d ≥ 1.
That means x % d ≤ d - 1
atMost7 |> N.remainderBy n3
--: N (In (Up0 minX_) (Up2 maxX_))Multiply by a given n ≥ 1.
That means x * n ≥ x
atLeast5 |> N.multiplyBy n2
--: N (Min (Up5 x_))
atLeast2 |> N.multiplyBy n5
--: N (Min (Up2 x_))Divide (//) by an N d ≥ 1
That means x / d ≤ x
atMost7
|> N.divideBy n3
--: N (In (Up0 minX_) (Up7 maxX_))
|> N.toIntClamp the number to between both given limits
between5And9 |> N.toIn ( n10, n10 )
--: N (In (Up10 minX_) (Up10 maxX_))
between5And15 |> N.toIn ( n5, n10 )
--: N (In (Up5 minX_) (Up10 maxX_))
between5And12 |> N.toIn ( n10, n12 )
--: N (In (Up10 minX_) (Up12 maxX_))
atLeast5 |> N.toIn ( n5, n10 )
--: N (In (Up5 minX_) (Up10 maxX_))
n15
|> N.toIn ( n10, n15 )
--: N (In (Up10 minX_) (Up15 max_))
|> N.toInt
--> 15
N.intToAtLeast n3 12345
|> N.toIn ( n3, N.intToIn ( n4, n5 ) 5 )
--: N (In (Up3 minX_) (Up5 maxX_))
|> N.toInt
--> 5There shouldn't be an upper limit? → toAtLeastMin
(The type doesn't forbid that the limits you're comparing against are beyond the current limits)
Cap the N to <= a given new upper limit –
but always keep it >= a given new lower limit.
n5AtLeast |> N.toInOrAtLeast ( n5, n10 )
--: N (In (Up5 x_) (On N10))
n11 |> N.minTo n5 |> N.toInOrAtLeast ( n5, n10 )
--: N (In (Up5 x_) (On N10))
--→ n10
-- ↓ edge cases if you don't set the new minimum as the current one
n7 |> N.toInOrAtLeast ( n6 |> N.minTo n5, n1 |> N.maxTo n10 )
--: N (In (Up5 x_) (On N10))
--→ n7
n5 |> N.toInOrAtLeast ( n6 |> N.minTo n5, n1 |> N.maxTo n10 )
--: N (In (Up5 x_) (On N10))
--→ n6In general, you always want to prefer toIn
and only use this version for type gymnastics
The type doesn't forbid that the upper limit you're comparing against is above the current upper limit
n10To14 |> N.toInOrAtLeast ( n10, n15 ) |> N.toInt
--: N (In (Up10 x_) (On N15))Not that you should do it, but if you don't set the minimum to the current minimum,
toInOrAtLeast behaves just like your regular toIn:
Is the N below than or at least as big as a given number?
vote :
{ age : N (In (On (Add18 minFrom18_)) max_) }
-> Vote
tryToVote { age } =
case age |> N.isAtLeast n18 of
Err _ ->
-- 😓
Nothing
Ok oldEnough ->
vote { age = oldEnough } |> Just
factorial : N (In min_ max_) -> N (Min (Up1 x_))
factorial =
factorialBody
factorialBody : N (In min_ max_) -> N (Min (Up1 x_))
factorialBody x =
case x |> N.isAtLeast n1 of
Err _ ->
n1 |> N.maxToInfinity
Ok atLeast1 ->
factorial (atLeast1 |> N.subtractMin n1)
|> N.multiplyBy 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_ (Up maxTo17 To N17)) } -> 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.)
The error result of comparing Ns
Above: greater than what it's compared againstBelow: less than what it's compared againstValues exist for each condition
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_ (Up17 maxX_)) } -> Toy
book :
{ age : N (In (On (Add19 minFrom9_)) max_) }
-> 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_ max_) -> Maybe (N (In (Up3 minX_) (Up10 maxX_)))
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.
The maximum of both given numbers in the same range
Even though you can just use directly
N.intToIn ( n0, n10 ) 3
|> N.greater (N.intToIn ( n0, n10 ) 1)
|> N.toInt
--> 3
N.intToIn ( n0, n10 ) 3
|> N.greater (N.intToIn ( n0, n10 ) 4)
|> N.toInt
--> 4this is rather supposed to be used as a primitive to build a structure maximum function
ArraySized.fold Up N.greaterFor clamping, try toIn and toAtLeastMin instead!
The minimum of both given numbers in the same range
Even though you can just use directly
N.intToIn ( n0, n10 ) 3
|> N.smaller (N.intToIn ( n0, n10 ) 1)
|> N.toInt
--> 1
N.intToIn ( n0, n10 ) 3
|> N.smaller (N.intToIn ( n0, n10 ) 4)
|> N.toInt
--> 3this is rather supposed to be used as a primitive to build a structure maximum function:
ArraySized.fold Up N.smallerFor clamping, try toIn instead!
More advanced stuff in section type information ) allowable-state
Drop the range constraints
to feed another library with its Int representation
n3
|> N.add n5
|> N.divideBy n3
|> N.toInt
--> 2Drop the range constraints
to feed another library with its Float representation
Equivalent to
toInt |> toFloatDrop the range constraints
to feed another library with its String representation
Equivalent to
toInt |> String.fromIntNumber with On range → number with equatable range
If you want an equatable Min ... N, you instead only need minToNumber
Set its minimum to a given (lower) one
[ atLeast3, atLeast4 ]Elm complains:
But all the previous elements in the list are:
N (Min N3)
[ atLeast3
, atLeast4 |> N.minTo n3
]To decrease its minimum by a relative amount, minSubtract
Decrease its minimum by a given relative amount.
To set its minimum to a specific absolute value, minTo
Decrease the start and end of its min difference
n3
--: N (In (Up3 (Add2 minX_)) (Up3 maxX_))
|> N.minEndsSubtract n2
--: N (In (Up5 minX_) (Up5 maxX_))maxEndsSubtract has an example of where this can be useful.
Set its maximum to a given (lower) one.
This can help "resetting" an argument's maximum to make it fit into functions that require a higher maximum.
Since you should type arguments and stored types as broad as possible
onlyAtMost18 : N (In min_ (Up maxTo18_ To N18)) -> ...
onlyAtMost18 between3And8 -- worksonce you implement onlyAtMost18, you might use the n in onlyAtMost19:
onlyAtMost18 n =
-- onlyAtMost19 n → error
onlyAtMost19 (n |> N.maxTo n18)To increase its maximum by a relative amount, maxAdd
On N (In min max)'s type,
allow max to go up to infinity to get a N (Min min)
between3And10 |> N.maxToInfinity
--: N (Min (Up3 x_))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 (Up1 x_))
[ atLeast1
, between1And10 |> N.maxToInfinity
]Increase its maximum by a given relative amount.
To set its maximum to a specific absolute value, N.maxTo
Decrease the start and end of its max difference
n3
--: N (In (Up3 minX_) (Up3 (Add2 maxX_)))
|> N.addStart n2
--: N (In (Up5 minX_) (Up5 maxX_))Huh... One rare use-case is avoiding an extra type parameter:
With extra type parameter:
{-| Gives each index a label. Access using `partIn`
-}
type alias Group record lastIndex count =
{ record : record
, parts : ArraySized String (Exactly (On count))
, lastIndex : N (In (On N0) lastIndex)
}
addPart :
String
->
(Group
(N (In (On N0) (Up x To lastIndexPlusX)) -> record)
(Up x To lastIndexPlusX)
count
-> Group record (Up x To (Add1 lastIndexPlusX)) (Add1 count)
)
addPart partName groupSoFar =
{ record = groupSoFar.record groupSoFar.lastIndex
, parts = groupSoFar.parts |> ArraySized.push partName
, lastIndex = groupSoFar.lastIndex |> N.add n1
}
partIn :
Group record i_ (Add1 lastIndex)
-> (record -> N (In (On N0) (Up x_ To lastIndex)))
-> String
partIn group field =
group.parts |> ArraySized.element ( Up, group.record |> field )If we change Group record lastIndex count to keep only one of both, we have a problem:
All indexes in the record are dependent on the same maximum which now is On.
That means that lower indexes aren't accepted by partIn anymore. The fix:
addPart :
String
->
(GroupBeingBuilt
(N (In (On N0) (Up (Add1 xFrom1) To lastIndex)) -> record)
(Up (Add1 xFrom1) To lastIndex)
count
-> GroupBeingBuilt record (Up xFrom1 To lastIndex) (Add1 count)
)
addPart partName groupSoFar =
{ record = groupSoFar.record groupSoFar.lastIndex
, parts = groupSoFar.parts |> ArraySized.push partName
, lastIndex = groupSoFar.lastIndex |> N.add n1 |> N.maxEndsSubtract n1 |> N.minTo n0
}
type alias GroupComplete record count =
Group record (On count) countThe really nice thing is that as we finish, the start of minimum difference is N0,
which makes this an On
Consider this an advanced technique for packages that use
allowable-state.
Any questions @lue in slack!
Transfer the knowledge about whether n0 is a possible value
stackRepeat :
element
-> N (In (On (N0OrAdd1 possiblyOrNever minFrom1_)) max_)
-> Emptiable (Stacked element) possiblyOrNever
stackRepeat toRepeat length =
case length |> N.isAtLeast1 of
Err possiblyOrNever ->
Emptiable.Empty possiblyOrNever
Ok lengthAtLeast1 ->
Stack.topBelow
toRepeat
(List.repeat
(lengthAtLeast1 |> N.subtract n1 |> N.toInt)
toRepeat
)
stackLength :
Emptiable (Stacked element) possiblyOrNever
-> N (Min (On (N0OrAdd1 possiblyOrNever N0)))
stackLength =
\stack ->
case stack of
Emptiable.Empty possiblyOrNever ->
-- adapt the variable minimum
n0 |> N.min0Adapt (\_ -> possiblyOrNever) |> N.maxToInfinity
Emptiable.Filled (TopBelow ( _, belowTop )) ->
belowTop
|> List.length
|> N.intToAtLeast n0
|> N.add n1
-- downgrade the minimum
|> N.min0Adapt neverusing N.min0Adapt
Stack and Emptiable are part of emptiness-typed
Cool, right?
Change the possiblyOrNever type for the case that its min is 0
never allows you to adapt any variable,
\_ -> yourVariablePossiblyOrNever swaps it for your given variable
stackLength :
Emptiable (Stacked element) possiblyOrNever
-> N (Min (On (N0OrAdd1 possiblyOrNever N0)))
stackLength =
\stack ->
case stack of
Emptiable.Empty possiblyOrNever ->
n0
-- adapt the variable minimum
|> N.min0Adapt (\_ -> possiblyOrNever)
-- allow a different min - 1 type
|> N.minAtLeast1Never
|> N.maxToInfinity
Emptiable.Filled (TopBelow ( _, belowTop )) ->
belowTop
|> List.length
|> N.intToAtLeast n0
|> N.add n1
-- downgrade the minimum
|> N.min0Adapt neverusing isAtLeast1, minAtLeast1Never.
Stack and Emptiable are part of emptiness-typed
with (\_ -> Possible) it's just a worse version of minSubtract
that might be useful in ultra rare situations
minSubtract1IfPossible :
N (In (On (N0OrAdd1 possiblyOrNever minFrom1)) max)
-> N (In (On (N0OrAdd1 Possibly minFrom1)) max)
minSubtract1IfPossible =
N.min0Adapt (\_ -> Possibly)For the case that its min is 1 + ..., allow adapting any variable.
This is possible because we currently have a minimum that is Never >= 1
stackLength :
Emptiable (Stacked element) possiblyOrNever
-> N (Min (On (N0OrAdd1 possiblyOrNever N0)))
stackLength =
\stack ->
case stack of
Emptiable.Empty possiblyOrNever ->
n0
-- adapt the variable minimum
|> N.min0Adapt (\_ -> possiblyOrNever)
-- allow a different type for min from 1
|> N.minAtLeast1Never
|> N.maxToInfinity
Emptiable.Filled (TopBelow ( _, belowTop )) ->
belowTop
|> List.length
|> N.intToAtLeast n0
|> N.add n1
-- downgrade the minimum
|> N.min0Adapt neverusing isAtLeast1, min0Adapt
Anything that can't be expressed with the available operations? → issue/PR
Internal parts you can safely access and transform
While the internally stored Int can't directly be guaranteed to be in bounds by elm,
the minimum and maximum
must be built as actual values checked by the compiler.
No shenanigans like runtime errors for impossible cases
Having those exposed can be useful when building extensions to this library like
Its limits
containing both its min
and its max
import Possibly exposing (Possibly(..))
N.intToIn ( n3, n5 ) 4
--: N (In (Up3 minX_) (Up5 maxX_))
|> N.minTo n2
--: N (In (Up2 minX_) (Up5 maxX_))
|> N.range
--: In (Up2 minX_) (Up5 maxX_)
|> N.rangeInToNumber
--: In N2 N5
|> N.rangeMin
--: N2
--> N.Add1 (N.Add1 (N.N0 Possible))The smallest allowed number promised by the range type as its representation as a difference
The greatest allowed number promised by the range type as its representation as a difference
Chain the difference Up to a higher natural number
Chain the difference Down to a lower number
Increase a number by a given difference
successor : n -> Add1 n
successor =
-- FYI: equivalent to Add1
N.addDifference (n1 |> N.min)
n10 |> N.min |> N.onToNumber |> successor
--> n11 |> N.min |> N.onToNumberDecrease the natural number by a given difference
predecessor : Add1 predecessor -> predecessor
predecessor =
N.subtractDifference (n1 |> N.min)
n10 |> N.min |> N.onToNumber |> predecessor
--> n9 |> N.min |> N.onToNumberBase type of N0, Add1 n following allowable-state:
Is the parameter possiblyOrNever set to Never like in
type alias Add1 n =
N0aOrAdd1 Never nN0 is impossible to construct
Is the parameter possiblyOrNever set to Possibly like in
type alias N0 =
N0OrAdd1 Possibly NeverN0 is a possible value
Create a range with a given representation = minimum = maximum
N.intToIn ( n3, n6 ) 5
|> N.min
|> N.exactlyRange
--: In (Up3 x) (Up3 x)Add a given difference to its minimum and maximum
Subtract its minimum and maximum by a given difference
Subtract its minimum by a given difference
Add a given difference to its maximum
Decrease the start and end of its rangeMin difference
n3
|> N.range
--: In (Up3 (Add2 minX_)) (Up3 maxX_)
|> N.rangeMinEndsSubtract (n2 |> N.range)
--: In (Up5 minX_) (Up5 maxX_)See maxEndsSubtract for details and examples
Decrease the start and end of its rangeMax difference
n3
|> N.range
--: In (Up3 minX_) (Up3 (Add2 maxX_))
|> N.rangeMaxEndsSubtract (n2 |> N.range)
--: In (Up5 minX_) (Up5 maxX_)See maxEndsSubtract for details and examples
Set its minimum to a given (lower) one
Set its maximum to a given (higher) one
Change the type
for the case that the N0OrAdd1 is Add1 from1
Change the type
for the case that the N0OrAdd1 is Add1 from1
Transfer the knowledge about whether n0 is a possible value
For the case that its min is 1 + ..., allow adapting any variable.
This is possible because we currently have a minimum that is Never >= 1
The N0OrAdd1 represented by this On difference
import Possibly exposing (Possibly(..))
N.intToIn ( n3, n10 ) 5
|> N.min
|> N.onToNumber
--> N.Add1 (N.Add1 (N.Add1 (N.N0 Possible)))useful
to preserve emptiness knowledge
predecessor :
N0OrAdd1 possiblyOrNever from1
-> Emptiable from1 possiblyOrNever
predecessor =
\number ->
case number of
N.N0 possiblyOrNever ->
Emptiable.Empty possiblyOrNever
N.Add1 predecessor ->
predecessor |> Emptiable.filledas a tag
type Element index
= Element index
element :
N (Exactly (On index))
-> Mapping (List element) (Element index) (Maybe element)
element index =
Typed.tag
(Element (index |> N.min |> N.onToNumber))
List.Extra.getAtCan be altered with addDifference, subtractDifference.
To preserve the ability to turn the number into an Int, use onToNumber
In (On ...) (On ...) → equatable In
equatable In → In (On ...) (On ...)
A bounded natural number
>= 0result type
The type variable enables adding, subtracting. Consider the "
Up" thing an implementation detailargument type
In (On (Add4 minFrom4_)) (Up maxTo15_ To N15)says:+some variable= 4+0|4+1|4+2|... which means it's ≥ 4+some variable= 15which means it's ≤ 15stored type
what to put in declared types like
ModelThere's also versions of this that don't contain functions internally:
more type examples at
In,Min