lue-bird / elm-bounded-nat / N

Natural number within a typed range


type N range

A bounded natural number >= 0

result type

-- ≥ 4
N (Min (Up4 x_))

-- 2 ≤ n ≤ 12
N (In (Up2 minX_) (Up12 maxX_))

-- n3 :
N (In (Up3 minX_) (Up3 maxX_))

The type variable enables adding, subtracting. Consider the "Up" thing an implementation detail

n3
    |> N.add n6
    --: N (In (Up9 minX_) (Up9 maxX_))
    |> N.multiplyBy n5
    --: N (Min (Up9 minX_))
    |> N.remainderBy n4
    --: N (In (Up0 minX_) (Up3 maxX_))
    |> N.inToNumber
--: N (In N0 N3)
--> n1 |> N.minTo n0 |> N.maxTo n3 |> N.inToNumber

argument type

-- ≥ 0, any limitations allowed
N range_

-- ≥ 4
N (In (On (Add4 minFrom4_)) max_)

-- 4 ≤ n ≤ 15
N (In (On (Add4 minFrom4_)) (Up maxTo15_ To N15))

In (On (Add4 minFrom4_)) (Up maxTo15_ To N15) says:

stored type

what to put in declared types like Model

-- ≥ 4
N (Min (On N4))

-- 2 ≤ n ≤ 12
N (In (On N2) (On N12))

There's also versions of this that don't contain functions internally:

-- ≥ 4
N (Min N4)

-- 2 ≤ n ≤ 12
N (In N2 N12)

more type examples at In, Min

bounds


type In minimumAsDifference maximumAsDifference

somewhere within a minimum & maximum

   ↓ minimum   ↓ maximum
⨯ [✓ ✓ ✓ ✓ ✓ ✓ ✓] ⨯ ⨯ ⨯...

argument type in a range

-- 3 ≤ n ≤ 5
N (In (On (Add3 minFrom3_)) (Up maxTo5_ To N5))

-- 0 ≤ n ≤ 100
percent : N (In min_ (Up maxTo100_ To N100)) -> Percent

For 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

result type in a range

n3 : N (In (Up3 minX_) (Up3 maxX_))

between3And6 : N (In (Up3 minX_) (Up6 maxX_))

between3And6 |> N.add n3
--: N (In (Up6 minX_) (Up9 maxX_))

stored type in a range

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)) element

Remember: ↑ 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,


type alias Min lowestPossibleAsDifference =
In lowestPossibleAsDifference Infinity

Only stored / result types should use Min

   ↓ minimum    ↓ or →
⨯ [✓ ✓ ✓ ✓ ✓ ✓ ✓...

result type without maximum constraint

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_))

argument type without maximum constraint

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_)

stored type without maximum constraint

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)) element

Remember: ↑ and other ... (On...) are result/stored types, not argument types

You can just use Min ( On ...) when you don't have disadvantages storing functions.

Can't store functions?


type Infinity
    = Infinity

"The limit is unknown".

Used in the definition of Min

type alias Min min =
    In minimum Infinity


type alias Exactly representation =
In representation representation

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 N3

This is pretty useless in combination with N

-- argument type
numberToInt : N (Exactly n) -> n -> Int
numberToInt wellThisWasPointless _ =
    wellThisWasPointless |> N.toInt

It 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


type alias On asNumber =
Up N0 To asNumber

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

Calling == 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


type Up lowNumber toTag highNumber
    = Difference ({ up : lowNumber -> highNumber, down : highNumber -> lowNumber })

Up low To high: a specific number represented as the difference high - low


type To
    = To Basics.Never

Just a word in a difference type:

Up low To high

Down high To low

→ distance high - low


type alias Down high toTag low =
Up low toTag high

Down high To low: a specific number represented as the difference high - low

create

inRandom : ( N (In minMin (Up minMaxToMaxMin_ To maxMin)), N (In (On maxMin) maxMax) ) -> Random.Generator (N (In minMin maxMax))

Generate a random N in a range

N.inRandom ( n1, n10 )
--: Random.Generator
--:     (N (In (Up1 minX_) (Up10 maxX_)))

inFuzz : ( N (In minMin (Up minMaxToMaxMin_ To maxMin)), N (In (On maxMin) maxMax) ) -> Fuzzer (N (In minMin maxMax))

Fuzzer for an N in a given range. For larger ranges, smaller Ns are preferred

import Fuzz

N.inFuzz ( n3, n6 )
    |> Fuzz.map N.toInt
    |> Fuzz.examples 10
--> [ 5, 6, 3, 3, 4, 6, 3, 6, 3, 5 ]

specific numbers

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


type alias N0 =
N0OrAdd1 Possibly Basics.Never

The natural number 0


type alias N1 =
N0OrAdd1 Basics.Never (N0OrAdd1 Possibly Basics.Never)

The natural number 1


type alias N2 =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Possibly Basics.Never))

The natural number 2


type alias N3 =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Possibly Basics.Never)))

The natural number 3


type alias N4 =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Possibly Basics.Never))))

The natural number 4


type alias N5 =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Possibly Basics.Never)))))

The natural number 5


type alias N6 =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Possibly Basics.Never))))))

The natural number 6


type alias N7 =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Possibly Basics.Never)))))))

The natural number 7


type alias N8 =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Possibly Basics.Never))))))))

The natural number 8


type alias N9 =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Possibly Basics.Never)))))))))

The natural number 9


type alias N10 =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Possibly Basics.Never))))))))))

The natural number 10


type alias N11 =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Possibly Basics.Never)))))))))))

The natural number 11


type alias N12 =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Possibly Basics.Never))))))))))))

The natural number 12


type alias N13 =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Possibly Basics.Never)))))))))))))

The natural number 13


type alias N14 =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Possibly Basics.Never))))))))))))))

The natural number 14


type alias N15 =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Possibly Basics.Never)))))))))))))))

The natural number 15


type alias Add1 n =
N0OrAdd1 Basics.Never n

The natural number 1 + a given n


type alias Add2 n =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never n)

The natural number 2 + a given n


type alias Add3 n =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never n))

The natural number 3 + a given n


type alias Add4 n =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never n)))

The natural number 4 + a given n


type alias Add5 n =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never n))))

The natural number 5 + a given n


type alias Add6 n =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never n)))))

The natural number 6 + a given n


type alias Add7 n =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never n))))))

The natural number 7 + a given n


type alias Add8 n =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never n)))))))

The natural number 8 + a given n


type alias Add9 n =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never n))))))))

The natural number 9 + a given n


type alias Add10 n =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never n)))))))))

The natural number 10 + a given n


type alias Add11 n =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never n))))))))))

The natural number 11 + a given n


type alias Add12 n =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never n)))))))))))

The natural number 12 + a given n


type alias Add13 n =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never n))))))))))))

The natural number 13 + a given n


type alias Add14 n =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never n)))))))))))))

The natural number 14 + a given n


type alias Add15 n =
N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never n))))))))))))))

The natural number 15 + a given n


type alias Up0 x =
Up x To x

0 as the difference Up x To x


type alias Up1 x =
Up x To (N0OrAdd1 Basics.Never x)

1 as the difference Up x To (N0OrAdd1 Possibly x)


type alias Up2 x =
Up x To (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never x))

2 as the difference Up x To (Add2 x)


type alias Up3 x =
Up x To (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never x)))

3 as the difference Up x To (Add3 x)


type alias Up4 x =
Up x To (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never x))))

4 as the difference Up x To (Add4 x)


type alias Up5 x =
Up x To (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never x)))))

5 as the difference Up x To (Add5 x)


type alias Up6 x =
Up x To (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never x))))))

6 as the difference Up x To (Add6 x)


type alias Up7 x =
Up x To (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never x)))))))

7 as the difference Up x To (Add7 x)


type alias Up8 x =
Up x To (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never x))))))))

8 as the difference Up x To (Add8 x)


type alias Up9 x =
Up x To (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never x)))))))))

9 as the difference Up x To (Add9 x)


type alias Up10 x =
Up x To (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never x))))))))))

10 as the difference Up x To (Add10 x)


type alias Up11 x =
Up x To (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never x)))))))))))

11 as the difference Up x To (Add11 x)


type alias Up12 x =
Up x To (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never x))))))))))))

12 as the difference Up x To (Add12 x)


type alias Up13 x =
Up x To (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never x)))))))))))))

13 as the difference Up x To (Add13 x)


type alias Up14 x =
Up x To (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never x))))))))))))))

14 as the difference Up x To (Add14 x)


type alias Up15 x =
Up x To (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never (N0OrAdd1 Basics.Never x)))))))))))))))

15 as the difference Up x To (Add15 x)

n0 : N (In (Up0 minX_) (Up0 maxX_))

The specific natural number 0

n1 : N (In (Up1 minX_) (Up1 maxX_))

The specific natural number 1

n2 : N (In (Up2 minX_) (Up2 maxX_))

The N 2

n3 : N (In (Up3 minX_) (Up3 maxX_))

The N 3

n4 : N (In (Up4 minX_) (Up4 maxX_))

The N 4

n5 : N (In (Up5 minX_) (Up5 maxX_))

The N 5

n6 : N (In (Up6 minX_) (Up6 maxX_))

The N 6

n7 : N (In (Up7 minX_) (Up7 maxX_))

The N 7

n8 : N (In (Up8 minX_) (Up8 maxX_))

The N 8

n9 : N (In (Up9 minX_) (Up9 maxX_))

The N 9

n10 : N (In (Up10 minX_) (Up10 maxX_))

The N 10

n11 : N (In (Up11 minX_) (Up11 maxX_))

The N 11

n12 : N (In (Up12 minX_) (Up12 maxX_))

The N 12

n13 : N (In (Up13 minX_) (Up13 maxX_))

The N 13

n14 : N (In (Up14 minX_) (Up14 maxX_))

The N 14

n15 : N (In (Up15 minX_) (Up15 maxX_))

The N 15

n16 : N (In (Up16 minX_) (Up16 maxX_))

The N 16

from Int

intToAbsolute : Basics.Int -> N (Min (Up0 x_))

The 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
--> 16

Really only use this if you want the absolute value

badLength =
    List.length >> N.intToAbsolute

intModBy : N (In (On (Add1 minFrom1_)) (Up maxX To (Add1 maxFrom1PlusX))) -> Basics.Int -> N (In (Up0 remainderMinX_) (Up maxX To maxFrom1PlusX))

Perform 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
--> 1

Works in the typical mathematical way for a negative Int

-7
    |> N.intModBy n3
    --: N (In (Up0 minX_) (Up2 maxX_))
    |> N.toInt
--> 2

Already have an N? → remainderBy

intToAtLeast : N (In min max_) -> Basics.Int -> N (Min min)

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
--> 9

You 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

intToIn : ( N (In minMin (Up minMaxToMaxMin_ To maxMin)), N (In (On maxMin) maxMax) ) -> Basics.Int -> N (In minMin maxMax)

Create a N (In ...) by clamping an Int between a minimum & maximum

If 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.toMaybe

Int compare

intIsAtLeast : N (In min max_) -> Basics.Int -> Result Basics.Int (N (Min min))

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 (Up5 x_)))
--> Err 4

1234 |> N.intIsAtLeast n5 |> Result.map N.toInt
--> Ok 1234

intIsIn : ( N (In minMin (Up minMaxToMaxMin_ To maxMin)), N (In (On maxMin) (Up maxMaxX To maxMaxPlusX)) ) -> Basics.Int -> Result (BelowOrAbove Basics.Int (N (Min (Up maxMaxX To (Add1 maxMaxPlusX))))) (N (In minMin (Up maxMaxX To maxMaxPlusX)))

Compared 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"

alter

add : N (In (Up minPlusX To sumMinPlusX) (Up maxPlusX To sumMaxPlusX)) -> N (In (Up minX To minPlusX) (Up maxX To maxPlusX)) -> N (In (Up minX To sumMinPlusX) (Up maxX To sumMaxPlusX))

Add a given specific N

between70And100 |> N.add n7
--: N (In (Up77 minX_) (Up107 maxX_))

One addend has an unconstrained maximum? → addMin

addMin : N (In (Up minPlusX To sumMinPlusX) addendMax_) -> N (In (Up minX To minPlusX) max_) -> N (Min (Up minX To sumMinPlusX))

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_))

subtract : N (In (Down maxPlusX To differenceMaxPlusX) (Down min To differenceMin)) -> N (In (On min) (Up maxX To maxPlusX)) -> N (In (On differenceMin) (Up maxX To differenceMaxPlusX))

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.inToNumber

One of the Ns has no maximum constraint? → N.subtractMin

The subtracted N can get greater than the current minimum? → N.subtractMax

subtractMin : N (In subtrahendMin_ (Down min To differenceMin)) -> N (In (On min) max) -> N (In (On differenceMin) max)

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 N1) (Up12 maxX_))

Use N.subtract if you want to subtract an N in a range or N.subtractMax if the subtracted N can get greater than the current minimum.

subtractMax : N (In (Down max To differenceMax) subtrahendMax_) -> N (In min_ (On max)) -> N (In (Up0 minX_) (On differenceMax))

From an N, subtract a number that can get greater than the current minimum

n5 |> N.subtractMax in3To7
--: N (In (Up0 minX_) (On N2))

Use N.subtract if you want to subtract an N in a range or N.subtractMin if one of the Ns has no maximum constraint

toPower : N (In (On (Add1 exponentMinFrom1_)) exponentMax_) -> N (In min max_) -> N (Min min)

N Raised to a given power p ≥ 1x ^ p ≥ x

atLeast5 |> N.toPower n2
--: N (Min (Up5 x_))

atLeast2 |> N.toPower n5
--: N (Min (Up2 x_))

remainderBy : N (In (On (Add1 divisorMinFrom1_)) (Up divMaxX To (Add1 divisorMaxFrom1PlusX))) -> N range_ -> N (In (Up0 remainderMinX_) (Up divMaxX To divisorMaxFrom1PlusX))

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_))

multiplyBy : N (In (On (Add1 multiplicandMinFrom1_)) multiplicandMax_) -> N (In min max_) -> N (Min min)

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_))

divideBy : N (In (On (Add1 divisorMinFrom1_)) divisorMax_) -> N (In min_ max) -> N (In (Up0 minX_) max)

Divide (//) by an N d ≥ 1 That means x / d ≤ x

atMost7
    |> N.divideBy n3
    --: N (In (Up0 minX_) (Up7 maxX_))
    |> N.toInt

clamp

toAtLeastMin : N (In min max_) -> N range_ -> N (Min min)

Cap the N to >= a given new lower limit

n5AtLeast |> N.toAtLeastMin n10
--: N (Min (Up10 x_))

The type doesn't forbid that the lower limit you're comparing against is below the current lower limit

n15AtLeast |> N.toAtLeastMin n10 |> N.toInt
--: N (Min (Up10 x_))

To clamp its maximum, too, toIn

toIn : ( N (In minMin (Up minNewMaxToMaxNewMin_ To maxMin)), N (In (On maxMin) maxMax) ) -> N range_ -> N (In minMin maxMax)

Clamp 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
--> 5

There shouldn't be an upper limit? → toAtLeastMin

(The type doesn't forbid that the limits you're comparing against are beyond the current limits)

toInOrAtLeast : ( N (In min (Up newMaxMinToMin_ To newMax)), N (In newMaxMin_ (On newMax)) ) -> N (In min max_) -> N (In min (On newMax))

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))
--→ n6

In 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:

compare

isAtLeast : N (In minMin (Up minMaxX To (Add1 minMaxFrom1PlusX))) -> N (In min max) -> Result (N (In min (Up minMaxX To minMaxFrom1PlusX))) (N (In minMin max))

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.)

isAtMost : N (In (Up maxMinX To maxMinPlusX) maxMax) -> N (In min max) -> Result (N (In (Up maxMinX To (Add1 maxMinPlusX)) max)) (N (In min maxMax))

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.)


type BelowOrAbove below above
    = Below below
    | Above above

The error result of comparing Ns

Values exist for each condition

is : N (In (Up minX To vsMinPlusX) (Up maxX To (Add1 vsMaxFrom1PlusX))) -> N (In min max) -> Result (BelowOrAbove (N (In min (Up maxX To vsMaxFrom1PlusX))) (N (In (Up minX To (Add1 vsMinPlusX)) max))) (N (In (Up minX To vsMinPlusX) (Up maxX To (Add1 vsMaxFrom1PlusX))))

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.)

isIn : ( N (In minMin (Up minMaxX To (Add1 minMaxFrom1PlusX))), N (In (Up maxMinX To maxMinPlusX) maxMax) ) -> N (In min max) -> Result (BelowOrAbove (N (In min (Up minMaxX To minMaxFrom1PlusX))) (N (In (Up maxMinX To (Add1 maxMinPlusX)) max))) (N (In minMin maxMax))

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:

.

greater : N range -> N range -> N range

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
--> 4

this is rather supposed to be used as a primitive to build a structure maximum function

ArraySized.fold Up N.greater

For clamping, try toIn and toAtLeastMin instead!

transform

toInt : N range_ -> Basics.Int

Drop the range constraints to feed another library with its Int representation

n3
    |> N.add n5
    |> N.divideBy n3
    |> N.toInt
--> 2

toFloat : N range_ -> Basics.Float

Drop the range constraints to feed another library with its Float representation

Equivalent to

toInt |> toFloat

toString : N range_ -> String

Drop the range constraints to feed another library with its String representation

Equivalent to

toInt |> String.fromInt

without internal functions

inToNumber : N (In (On min) (On max)) -> N (In min max)

Number with On range → number with equatable range

If you want an equatable Min ... N, you instead only need minToNumber

inToOn : N (In min max) -> N (In (On min) (On max))

N with equatable number rangeN with On range to be altered, compared, ...

If you want a Min (On ...) N, you instead only need minToOn

minToNumber : N (In (On min) max) -> N (In min max)

N with On minimum → N with equatable minimum number

You'll usually use this to convert to an equatable Min ... N

minToOn : N (In min max) -> N (In (On min) max)

N with equatable minimum number → N with On minimum

You'll usually use this to convert to a Min (On ...) N

maxToNumber : N (In min (On max)) -> N (In min max)

N with On maximum → N with equatable maximum number

maxToOn : N (In min max) -> N (In min (On max))

N with an equatable maximum number → N with On maximum

type information

minTo : N (In minNew (Up minNewMaxToMin_ To min)) -> N (In (On min) max) -> N (In minNew max)

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

minSubtract : N (In maxIncreasedMin_ (Down minPlusX To minDecreasedPlusX)) -> N (In (Up x To minPlusX) max) -> N (In (Up x To minDecreasedPlusX) max)

Decrease its minimum by a given relative amount.

To set its minimum to a specific absolute value, minTo

minEndsSubtract : N (In (Down minX To minXDecreased) (Down minPlusX To minPlusXDecreased)) -> N (In (Up minX To minPlusX) max) -> N (In (Up minXDecreased To minPlusXDecreased) max)

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.

minTo0 : N (In min_ max) -> N (In (Up0 minX_) max)

Set the minimum as 0, without relying on the minimum being fixed like the more general minTo

maxTo : N (In (On newMaxMin) newMax) -> N (In min (Up maxToNewMaxMin_ To newMaxMin)) -> N (In min newMax)

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 -- works

once 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

maxToInfinity : N (In min max_) -> N (Min min)

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
]

maxAdd : N (In increaseMin_ (Up maxPlusX To maxIncreasedPlusX)) -> N (In min (Up maxX To maxPlusX)) -> N (In min (Up maxX To maxIncreasedPlusX))

Increase its maximum by a given relative amount.

To set its maximum to a specific absolute value, N.maxTo

maxEndsSubtract : N (In (Down maxPlusX To maxPlusXDecreased) (Down maxX To maxXDecreased)) -> N (In min (Up maxX To maxPlusX)) -> N (In min (Up maxXDecreased To maxPlusXDecreased))

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) count

The really nice thing is that as we finish, the start of minimum difference is N0, which makes this an On

allowable-state

Consider this an advanced technique for packages that use allowable-state. Any questions

isAtLeast1 : N (In (On (N0OrAdd1 n0PossiblyOrNever minFrom1_)) max) -> Result n0PossiblyOrNever (N (In (Up1 minX_) max))

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 never

using N.min0Adapt

Stack and Emptiable are part of emptiness-typed

Cool, right?

min0Adapt : (possiblyOrNever -> adaptedPossiblyOrNever) -> N (In (On (N0OrAdd1 possiblyOrNever minFrom1)) max) -> N (In (On (N0OrAdd1 adaptedPossiblyOrNever minFrom1)) max)

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 never

using 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)

minAtLeast1Never : N (In (On (N0OrAdd1 possiblyOrNever Basics.Never)) max) -> N (In (On (N0OrAdd1 possiblyOrNever minFrom1_)) max)

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 never

using isAtLeast1, min0Adapt

miss an operation?

Anything that can't be expressed with the available operations? → issue/PR

safe internals

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

range : N range -> range

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))

min : N (In min maximum_) -> min

The smallest allowed number promised by the range type as its representation as a difference

max : N (In min_ max) -> max

The greatest allowed number promised by the range type as its representation as a difference

differenceAdd : Up middle To high -> Up low To middle -> Up low To high

Chain the difference Up to a higher natural number

differenceSubtract : Down high To middle -> Up low To high -> Up low To middle

Chain the difference Down to a lower number

addDifference : Up low To high -> low -> high

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.onToNumber

subtractDifference : Down high To low -> high -> low

Decrease 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.onToNumber


type N0OrAdd1 n0PossiblyOrNever from1
    = N0 n0PossiblyOrNever
    | Add1 from1

Base type of N0, Add1 n following allowable-state:

ranges

inRange : ( In minMin (Up minMaxToMaxMin_ To maxMin), In (On maxMin) maxMax ) -> In minMin maxMax

Create a range from the lowest possible representation of a given lower range to the highest possible representation of a given higher range

minRange : min -> Min min

Create a range with a given representation as the minimum and Infinity as the maximum

N.intToIn ( n3, n6 ) 5
    |> N.min
    |> N.minRange
--: Min (Up3 x)

maxRange : max -> In (Up0 newMinX_) max

Create a range with minimum set to 0 and a given maximum

exactlyRange : limitRepresentation -> In limitRepresentation limitRepresentation

Create a range with a given representation = minimum = maximum

N.intToIn ( n3, n6 ) 5
    |> N.min
    |> N.exactlyRange
--: In (Up3 x) (Up3 x)

rangeMin : In min max_ -> min

The range's lowest possible representation

rangeMax : In min_ max -> max

The range's highest possible representation

rangeAdd : In (Up minPlusX To minIncreasedPlusX) (Up maxPlusX To maxIncreasedPlusX) -> In (Up minX To minPlusX) (Up maxX To maxPlusX) -> In (Up minX To minIncreasedPlusX) (Up maxX To maxIncreasedPlusX)

Add a given difference to its minimum and maximum

rangeSubtract : In (Down maxPlusX To maxDecreasedPlusX) (Down min To minDecreased) -> In (On min) (Up maxX To maxPlusX) -> In (On minDecreased) (Up maxX To maxDecreasedPlusX)

Subtract its minimum and maximum by a given difference

rangeMinSubtract : Down minPlusX To minDecreasedPlusX -> In (Up x To minPlusX) max -> In (Up x To minDecreasedPlusX) max

Subtract its minimum by a given difference

rangeMaxSubtract : Down maxPlusX To maxDecreasedPlusX -> In min_ (Up x To maxPlusX) -> In (Up0 minX_) (Up x To maxDecreasedPlusX)

Subtract its maximum by a given difference and set the minimum to 0

rangeMaxAdd : Up maxPlusX To maxIncreasedPlusX -> In min (Up maxX To maxPlusX) -> In min (Up maxX To maxIncreasedPlusX)

Add a given difference to its maximum

rangeMinEndsSubtract : In (Down minX To minXDecreased) (Down minPlusX To minPlusXDecreased) -> In (Up minX To minPlusX) max -> In (Up minXDecreased To minPlusXDecreased) max

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

rangeMaxEndsSubtract : In (Down maxPlusX To maxPlusXDecreased) (Down maxX To maxXDecreased) -> In min (Up maxX To maxPlusX) -> In min (Up maxXDecreased To maxPlusXDecreased)

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

rangeMinTo : In newMin (Up newMinMaxToMin_ To min) -> In (On min) max -> In newMin max

Set its minimum to a given (lower) one

rangeMaxTo : In (On newMaxMin) maxNew -> In min (Up maxToNewMaxMin_ To newMaxMin) -> In min maxNew

Set its maximum to a given (higher) one

allowable-state

number0Adapt : (n0PossiblyOrNever -> adaptedN0PossiblyOrNever) -> N0OrAdd1 n0PossiblyOrNever from1 -> N0OrAdd1 adaptedN0PossiblyOrNever from1

Change the possiblyOrNever type for the case that the N0OrAdd1 is N0

numberFrom1Map : (from1 -> mappedFrom1) -> N0OrAdd1 n0PossiblyOrNever from1 -> N0OrAdd1 n0PossiblyOrNever mappedFrom1

Change the type for the case that the N0OrAdd1 is Add1 from1

on0Adapt : (possiblyOrNever -> adaptedPossiblyOrNever) -> On (N0OrAdd1 possiblyOrNever from1) -> On (N0OrAdd1 adaptedPossiblyOrNever from1)

Change the possiblyOrNever type for the case that its N0OrAdd1 representation is N0

onFrom1Map : (from1 -> mappedFrom1) -> On (N0OrAdd1 n0PossiblyOrNever from1) -> On (N0OrAdd1 n0PossiblyOrNever mappedFrom1)

Change the type for the case that the N0OrAdd1 is Add1 from1

rangeIsAtLeast1 : N (In (On (N0OrAdd1 n0PossiblyOrNever minFrom1_)) max) -> Result n0PossiblyOrNever (In (Up1 minX_) max)

Transfer the knowledge about whether n0 is a possible value

rangeMin0Adapt : (possiblyOrNever -> adaptedPossiblyOrNever) -> In (On (N0OrAdd1 possiblyOrNever minFrom1)) max -> In (On (N0OrAdd1 adaptedPossiblyOrNever minFrom1)) max

Change its minimum's possiblyOrNever type for the case that the N0OrAdd1 is N0

rangeMinAtLeast1Never : In (On (N0OrAdd1 possiblyOrNever Basics.Never)) max -> In (On (N0OrAdd1 possiblyOrNever minFrom1_)) max

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

safe internals without functions

onToNumber : On representedNumber -> representedNumber

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

Can be altered with addDifference, subtractDifference.

To preserve the ability to turn the number into an Int, use onToNumber

toOn : asNumber -> On asNumber

equatable number → On

rangeInToNumber : In (On min) (On max) -> In min max

In (On ...) (On ...) → equatable In

rangeInToOn : In min max -> In (On min) (On max)

equatable InIn (On ...) (On ...)

rangeMinToNumber : In (On min) max -> In min max

Make range's On minimum equatable

rangeMinToOn : In min max -> In (On min) max

Make range's equatable minimum On

rangeMaxToNumber : In min (On max) -> In min max

Make range's On maximum equatable

rangeMaxToOn : In min max -> In min (On max)

Make range's equatable maximum On