lue-bird/elm-typesafe-array/28.0.0

NOTE: the latest version is 34.0.1

ArraySized

An Array that knows more about the amount of elements it holds

import Linear exposing (Direction(..))
import N exposing (n0)
import Array


Array.empty |> Array.get 0
--> Nothing


ArraySized.empty |> ArraySized.element ( Up, n0 )
-- compile-time error

Is this any useful? One example:

You have an array of 1+ elements. What's its greatest value?

withArray : Array comparable -> Maybe comparable
withArray =
    Array.foldl
        (\element soFar ->
            case soFar of
                Just maxSoFar ->
                    max maxSoFar element


                Nothing ->
                    element
        )
        Nothing


withArraySized :
    ArraySized comparable (In (On (Add1 minFrom1_)) max_)
    -> comparable
withArraySized =
    ArraySized.fold Up Basics.max

The Array type can't express it contains 1+ elements. ArraySized knows about its length at compile time, so we can fold, access, ... without a worry

type alias ArraySized element lengthRange =
ArraySized element lengthRange

An Array that knows+ about the amount of elements it holds

result type

-- length >= 5
: ArraySized ... (Min (Up5 x_))


-- 2 <= length <= 12
: ArraySized ... (In (Up2 minX_) (Up12 maxX_))

Representing a result's numbers as this weird Up<n> x is what allows the little magic tricks in the library: attaching, taking, dropping, chunking, comparing, ...

argument type

-- length = 15
: ArraySized ... (Exactly (On N15))


-- length >= 4
: ArraySized ... (In (On (Add4 minFrom4_)) max_)


-- 4 <= length <= 15
: ArraySized ...
:     (In (On (Add4 minFrom4_)) (Up maxTo15_ To N15))

to allow the broadest range of desired lengths,

  • to require nothing about the upper limit → leave the maximum as a variable
  • fix lower limits to the desired minimum number + some variable
  • require the actual upper limit to go Up a variable amount to arrive at the desired maximum number

stored type

in your Model for example. They look just like result types but every Up<n> x becomes On N<n>, avoiding type variables

-- length >= 4
: ArraySized ... (Min (On N4))


-- 4 <= length <= 15
: ArraySized ... (InOn N4 N15)


-- length = 15
: ArraySized ... (Exactly (On N15))

InOn, Exactly are just shorthands exposed from N

InOn min max =
    In (On min) (On max)


Exactly n =
    In n n

== on both ArraySizeds and Ns crashes elm. Compare safely or convert toNumber

create

repeat : element -> N range -> ArraySized element range

A given amount of same elements

import N exposing (n4)


ArraySized.repeat 'L' n4
    --: ArraySized Char (In (Up4 minX_) (Up4 maxX_))
    |> ArraySized.toList
--> [ 'L', 'L', 'L', 'L' ]


ArraySized.repeat 'L' atLeast3
--: Char ArraySized (Min (Up3 x_))
N (In (On min) (Up maxX To maxPlusX))
-> ArraySized
(N (In (Up0 nMinX_) (Up maxX To maxPlusX)))
(In (On (Add1 min)) (Up maxX To (Add1 maxPlusX)))

Increasing natural numbers until including a given number

import N exposing (n3, n0)


ArraySized.upTo n3
--: ArraySized
--:     (N (In (Up0 minX_) (Up3 maxX_)))
--:     (In (On N4) (Up4 maxX_))
    |> ArraySized.map N.toInt
    |> ArraySized.toList
--> [ 0, 1, 2, 3 ]


ArraySized.upTo n0
--: ArraySized
--:     (N (In (Up0 minX_) (Up0 maxX_)))
--:     (In (On N1) (Up1 maxX_))
    |> ArraySized.map N.toInt
    |> ArraySized.toList
--> [ 0 ]


ArraySized.upTo between2And9
    |> ArraySized.map (N.add n3)
--: ArraySized
--:    (N (In (Up5 minX_) (Up12 maxX_)))
--:    (In (On N3) (Up10 maxX_))

To turn the On length minimum into a difference (for results etc.) → minTo

Generator element
-> N range
-> Generator (ArraySized element range)

Random.Generator for a given amount of random elements

import N exposing (n5)


ArraySized.random (Random.float 0 1) n5
--: Random.Generator
--:     (ArraySized
--:         Float
--:         (In (Up5 minX_) (Up5 maxX_))
--:     )

Pairs really well with

N.randomIn ( <length min>, <length max> )
    |> Random.andThen
        (ArraySized.random <element>)
fuzz : Fuzzer element -> N range -> Fuzzer (ArraySized element range)

Fuzzer for an ArraySized with a given length

import N exposing (n3)
import Fuzz


ArraySized.fuzz Fuzz.bool n3
    |> Fuzz.map ArraySized.toList
    --: Fuzzer (ArraySized Bool (In (Up3 minX_) (Up6 maxX_)))
    |> Fuzz.examples 3
--> [ [ False, True, False ]
--> , [ False, False, False ]
--> , [ False, False, True ]
--> ]

To fuzz an ArraySized with a length in a range, inFuzz

Fuzzer element
-> ( N
(In
lowerLimitMin
(Up lowerLimitMaxToUpperLimitMin_ To upperLimitMin)
)
, N (In (On upperLimitMin) upperLimitMax)
)
-> Fuzzer (ArraySized element (In lowerLimitMin upperLimitMax))

Fuzzer for an ArraySized with a length in a given range. For larger ranges, smaller lengths are preferred

import N exposing (n3, n6)
import Fuzz


ArraySized.inFuzz Fuzz.bool ( n3, n6 )
    |> Fuzz.map ArraySized.toList
    --: Fuzzer (ArraySized Bool (In (Up3 minX_) (Up6 maxX_)))
    |> Fuzz.examples 3
--> [ [ False, True, False, False, True, True ]
--> , [ False, False, False, True ]
--> , [ False, True, False, False, False, False ]
--> ]
fromArray : Array element -> ArraySized element (Min (Up0 x_))

Create from an Array. As every Array has >= 0 elements

arrayFromSomeOtherLibrary |> ArraySized.fromArray
--: ArraySized ... (Min (Up0 x_))

Don't use it for construction

ArraySized.fromArray
    (Array.fromList [ 0, 1, 2, 3, 4, 5, 6 ])
-- big no

Make sure the compiler knows as much as you about the amount of elements!

ArraySized.l7 0 1 2 3 4 5 6 -- ok


ArraySized.upTo n6 -- big yes

"wrap early, unwrap late"

fromList : List element -> ArraySized element (Min (Up0 minX_))

Create from a List. As every List has >= 0 elements

listFromSomeOtherLibrary |> ArraySized.fromList
--: ArraySized ... (Min (Up0 x_))

Don't use for construction

ArraySized.fromList [ 0, 1, 2, 3, 4, 5, 6 ]
-- big no!

Make sure the compiler knows as much as you about the amount of elements!

ArraySized.l7 0 1 2 3 4 5 6 -- ok


ArraySized.upTo n6 -- big yes

"wrap early, unwrap late"

Emptiable element possiblyOrNever
-> ArraySized
element
(In (On (N0OrAdd1 possiblyOrNever N0)) (Up1 maxX_))

On Emptiable.filled ArraySized.one, on Emptiable.empty ArraySized.empty

import N exposing (n0)
import Emptiable exposing (filled)


filled "hi"
    |> ArraySized.fromEmptiable
    --: ArraySized ... (In (Up0 minX_) (Up1 maxX_))
    |> ArraySized.toList
--> [ "hi" ]


Emptiable.empty
    |> ArraySized.fromEmptiable
    --: ArraySized ... (In (Up0 minX_) (Up1 maxX_))
    |> ArraySized.toList
--> []

Emptiness knowledge possiblyOrNever is transferred

Emptiable (Stacked element) possiblyOrNever
-> ArraySized element (Min (On (N0OrAdd1 possiblyOrNever N0)))

Create from a stack. As every stack has >= 0 elements

Emptiable.empty |> ArraySized.fromStack
--: ArraySized ... (Min (On N0))


Stack.topBelow '#' [] |> ArraySized.fromStack
--: ArraySized Char (Min (On N1))

Don't use for construction

ArraySized.fromStackEmptiable
    (Stack.fromList [ 0, 1, 2, 3, 4, 5, 6 ])
-- big no!

Make sure the compiler knows as much as you about the amount of elements!

ArraySized.l7 0 1 2 3 4 5 6 -- ok


ArraySized.upTo n6 -- big yes

"wrap early, unwrap late"

Got an Emptiable (Stacked ...) Never? → fromStackFilled

fromString : String -> ArraySized Char (Min (Up0 minX_))

Create from the Chars of a String. As every String has >= 0 elements

stringFromSomeOtherLibrary |> ArraySized.fromString
--: ArraySized Char (Min (Up0 minX_))

Try not to use this for construction and instead use the safe versions like l<n>

specific length

empty : ArraySized element_ (In (Up0 minX_) (Up0 maxX_))

No elements

ArraySized.empty
--: ArraySized element_ (In (Up0 minX_) (Up0 maxX_))
    |> ArraySized.push ":)"
    --: ArraySized String (In (Up1 minX_) (Up1 maxX_))
one : element -> ArraySized element (In (Up1 minX_) (Up1 maxX_))

Create with only 1 single given element

l2 :
element
-> element
-> ArraySized element (In (Up2 minX_) (Up2 maxX_))

Create with 2 given elements in the order they are supplied

l3 :
element
-> element
-> element
-> ArraySized element (In (Up3 minX_) (Up3 maxX_))

Create with 3 given elements in the order they are supplied

l4 :
element
-> element
-> element
-> element
-> ArraySized element (In (Up4 minX_) (Up4 maxX_))

Create with 4 given elements in the order they are supplied

l5 :
element
-> element
-> element
-> element
-> element
-> ArraySized element (In (Up5 minX_) (Up5 maxX_))

Create with 5 given elements in the order they are supplied

l6 :
element
-> element
-> element
-> element
-> element
-> element
-> ArraySized element (In (Up6 minX_) (Up6 maxX_))

Create with 6 given elements in the order they are supplied

l7 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized element (In (Up7 minX_) (Up7 maxX_))

Create with 7 given elements in the order they are supplied

l8 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized element (In (Up8 minX_) (Up8 maxX_))

Create with 8 given elements in the order they are supplied

l9 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized element (In (Up9 minX_) (Up9 maxX_))

Create with 9 given elements in the order they are supplied

l10 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized element (In (Up10 minX_) (Up10 maxX_))

Create with 10 given elements in the order they are supplied

l11 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized element (In (Up11 minX_) (Up11 maxX_))

Create with 11 given elements in the order they are supplied

l12 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized element (In (Up12 minX_) (Up12 maxX_))

Create with 12 given elements in the order they are supplied

l13 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized element (In (Up13 minX_) (Up13 maxX_))

Create with 13 given elements in the order they are supplied

l14 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized element (In (Up14 minX_) (Up14 maxX_))

Create with 14 given elements in the order they are supplied

l15 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized element (In (Up15 minX_) (Up15 maxX_))

Create with 15 given elements in the order they are supplied

l16 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized element (In (Up16 minX_) (Up16 maxX_))

Create with 16 given elements in the order they are supplied

You can generate l<n> with n >= 17 locally, put them in a module ArraySized.Local exposing (l<n>, ...) + import ArraySized.Local as ArraySized

scan

length : ArraySized element_ range -> N range

Its amount of elements

import N exposing (n3)


ArraySized.l3 1 2 3
    |> ArraySized.length
    --: N (In (Up3 minX_) (Up3 maxX_))
    |> N.toInt
--> 3


between3And5Elements |> ArraySized.length
--: N (In (Up3 minX_) (Up5 maxX_))


atLeast3Elements |> ArraySized.length
--: N (Min (Up3 minX_))
( Direction
, N (In indexMin_ (Up indexMaxToMinFrom1_ To minFrom1))
)
-> ArraySized element (In (On (Add1 minFrom1)) max_)
-> element

Its element at a valid location in a given Direction

import Linear exposing (Direction(..))
import N exposing (n1)


ArraySized.l4 0 1 2 3
    |> ArraySized.element ( Up, n1 )
--> 1


ArraySized.l4 0 1 2 3
    |> ArraySized.element ( Down, n1 )
--> 2
( Direction, N range_ )
-> ArraySized element lengthRange_
-> Emptiable element Possibly

Its element at a given location in a given Direction. Because the index doesn't promise it's <= the ArraySized's length minimum, elementTry gives back an Emptiable

Use element if you know your index always points to a valid location

import Linear exposing (Direction(..))
import Emptiable
import N exposing (n1, n5)


ArraySized.l4 0 1 2 3
    |> ArraySized.elementTry ( Up, n5 )
--> Emptiable.empty


ArraySized.l4 0 1 2 3
    |> ArraySized.elementTry ( Down, n1 )
--> Emptiable.filled 2
ArraySized
element
(In (On (Add1 minFrom1_)) (Up maxTo1_ To N1))
-> element

Its one and only, first and last element

ArraySized.one "hi" |> ArraySized.toOne
--> "hi"

Searching for all, any? → allFill

alter

( Direction, N indexRange_ )
-> (() -> element)
-> ArraySized element range
-> ArraySized element range

Set the element at an index in a given Direction

import Linear exposing (Direction(..))
import N exposing (n1, n2)


ArraySized.l3 "I" "am" "ok"
    |> ArraySized.elementReplace ( Up, n2 )
        (\() -> "confusion")
    |> ArraySized.toList
--> [ "I", "am", "confusion" ]


ArraySized.l3 "I" "am" "ok"
    |> ArraySized.elementReplace ( Down, n1 )
        (\() -> "feel")
    |> ArraySized.toList
--> [ "I", "feel", "ok" ]

An index that's too high to point to an existing element is ignored and no element is replaced

import Linear exposing (Direction(..))
import N exposing (n3)


ArraySized.l3 "I" "am" "ok"
    |> ArraySized.elementReplace ( Down, n3 )
        (\() -> "feel")
    |> ArraySized.toList
--> [ "I", "am", "ok" ]
( Direction, N indexRange_ )
-> (element -> element)
-> ArraySized element range
-> ArraySized element range

Change the element at an index in a given Direction based on its previous value

import Linear exposing (Direction(..))
import N exposing (n0)


ArraySized.l3 1 20 30
    |> ArraySized.elementAlter ( Up, n0 ) (\x -> x * 10)
    |> ArraySized.toList
--> [ 10, 20, 30 ]


ArraySized.l3 1 20 30
    |> ArraySized.elementAlter ( Down, n0 ) negate
    |> ArraySized.toList
--> [ 1, 20, -30 ]

An index that's too high to point to an existing element is ignored and no element is altered

import Linear exposing (Direction(..))
import N exposing (n3)


ArraySized.l3 1 20 30
    |> ArraySized.elementAlter ( Up, n3 ) (\x -> x * 10)
    |> ArraySized.toList
--> [ 1, 20, 30 ]
reverse : ArraySized element range -> ArraySized element range

Flip the order of the elements

ArraySized.l4 "l" "i" "v" "e"
    |> ArraySized.reverse
    |> ArraySized.toList
--> [ "e", "v", "i", "l" ]
element
-> ArraySized
element
(In (Up minX To minPlusX) (Up maxX To maxPlusX))
-> ArraySized
element
(In (Up minX To (Add1 minPlusX)) (Up maxX To (Add1 maxPlusX)))

Put a new element after all the others

between5And10Elements
    |> ArraySized.push "becomes the last"
--: ArraySized String (In (Up6 minX_) (Up11 maxX_))

pushMin if you don't know the length maximum

element
-> ArraySized
element
(In (Up minX To minPlusX) (Up maxX_ To maxPlusX_))
-> ArraySized element (Min (Up minX To (Add1 minPlusX)))

Put a new element after all the others

atLeast5Elements
    |> ArraySized.pushMin "becomes the last"
--: ArraySized String (Min (Up6 minX_))

push if you know the length maximum

( Direction, N (In indexMin_ (Up indexMaxToMin_ To min)) )
-> element
-> ArraySized element (In (On min) (Up maxX To maxPlusX))
-> ArraySized
element
(In (On (Add1 min)) (Up maxX To (Add1 maxPlusX)))

Put an element in the ArraySized at a given index in a given Direction

import Linear exposing (Direction(..))
import N exposing (n1, n2)


ArraySized.l3 'a' 'c' 'd'
    |> ArraySized.insert ( Up, n1 ) 'b'
    --: ArraySized Char (In (On N4) (Up4 maxX_))
    |> ArraySized.toList
--> [ 'a', 'b', 'c', 'd' ]


ArraySized.l3 'a' 'c' 'd'
    |> ArraySized.insert ( Down, n2 ) 'b'
    |> ArraySized.toList
--> [ 'a', 'b', 'c', 'd' ]

insertMin if you don't know the length maximum

Need the length minimum to not become On (for results etc.) → |> minTo

( Direction, N (In indexMin_ (Up indexMaxToMin_ To min)) )
-> element
-> ArraySized element (In (On min) (Up x_ To maxPlusX_))
-> ArraySized element (Min (On (Add1 min)))

Put a new element at an index in a given Direction

import Linear exposing (Direction(..))
import N exposing (n0, n1)


atLeast5Elements
    |> ArraySized.insertMin ( Down, n1 ) "before last"
    --: ArraySized String (Min (On N6))


minCons :
    element
    -> ArraySized element (In (On min) max_)
    -> ArraySized element (Min (On (Add1 min)))
minCons =
    ArraySized.insertMin ( Up, n0 )

insert if you know the length maximum

Need the length minimum to not become On (for results etc.) → |> minTo

( Direction
, N (In indexMin_ (Up indexMaxToMinFrom1_ To minFrom1))
)
-> ArraySized
element
(In (On (Add1 minFrom1)) (Up maxX To (Add1 maxMinus1PlusX)))
-> ArraySized
element
(In (On minFrom1) (Up maxX To maxMinus1PlusX))

Kick out the element at a given index in a given Direction

import Linear exposing (Direction(..))
import N exposing (n0)


removeLast between1And10Elements =
    between1And10Elements
        |> ArraySized.remove ( Down, n0 )
  • Don't know the length maximum? → removeMin
  • Want to remove an element beyond the length minimum? → removeMin
( Direction, N indexRange_ )
-> ArraySized element (In (On (Add1 minFrom1)) max)
-> ArraySized element (In (On minFrom1) max)

Kick out the element at an index in a given Direction

removeLast =
    ArraySized.removeMin ( Down, n0 )

This only works when the ArraySized has at minimum 1 element. To maybe remove an element, match on ArraySized.hasAtLeast n1

  • Know the length maximum? → remove
  • Want to make the length minimum a difference again (not On) for results, ...? → ArraySized.minTo

filter

ArraySized
(Emptiable value possiblyOrNever_)
(In (On min_) max)
-> ArraySized value (In (Up0 minX_) max)

Take every filled value, drop every empty

import Emptiable exposing (filled)


ArraySized.l3 ("This" |> filled) Emptiable.empty ("fine" |> filled)
    |> ArraySized.fills
    --: ArraySized String (In (Up0 minX_) (Up3 maxX_))
    |> ArraySized.toList
--> [ "This", "fine" ]

map |> fills to get the same functionality as "filterMap"

import Emptiable


ArraySized.l3 "1.2" "2" "hello"
    |> ArraySized.map (String.toInt >> Emptiable.fromMaybe)
    |> ArraySized.fills
    |> ArraySized.toList
--> [ 2 ]
ArraySized (Emptiable value possiblyOrNever) range
-> Emptiable (ArraySized value range) possiblyOrNever

If every Emptiable is filled, all of the values. If any element is empty, empty

import Emptiable exposing (filled)


ArraySized.empty
    |> ArraySized.allFill
    |> Emptiable.map ArraySized.toList
--> filled []


ArraySized.l3 (filled 1) (filled 2) (filled 3)
    |> ArraySized.allFill
    |> Emptiable.map ArraySized.toList
--> filled [ 1, 2, 3 ]


ArraySized.l3 (filled 1) Emptiable.empty (filled 3)
    |> ArraySized.allFill
--> Emptiable.empty

Can also be used to check whether all/any elements satisfy a given test

import Possibly exposing (Possibly)
import Emptiable exposing (Emptiable)




atMost4 : Int -> Emptiable Int Possibly
atMost4 =
    \n ->
        if n <= 4 then
            n |> Emptiable.filled
        else
            Emptiable.empty


-- all




ArraySized.l2 2 3
    |> ArraySized.map atMost4
    |> ArraySized.allFill
    |> (/=) Emptiable.empty
--> True


ArraySized.l2 2 7
    |> ArraySized.map atMost4
    |> ArraySized.allFill
    |> (/=) Emptiable.empty
--> False




-- any




ArraySized.l2 -5 300
    |> ArraySized.map atMost4
    |> ArraySized.allFill
    |> (==) Emptiable.empty
--> True


ArraySized.l2 0 0
    |> ArraySized.map atMost4
    |> ArraySized.allFill
    |> (==) Emptiable.empty
--> False

Q: Why not expose any, all?

A: to push you towards parsing, not validating

Funny aside, allFill can sometimes even be nicer than mapN/andMap

groupCall =
    ArraySized.l5 aUser bUser cUser dUser eUser
        |> ArraySized.map .phoneNumber
        |> ArraySized.allFill


-- vs
groupCall =
    map5 ArraySized.l5
        aUser.phoneNumber
        bUser.phoneNumber
        cUser.phoneNumber
        dUser.phoneNumber
        eUser.phoneNumber

part

Direction
-> { atLeast : N (In takenMin (Up takenMaxToMin_ To min)) }
-> N (In takenMin takenMax)
-> ArraySized element (In (On min) max_)
-> ArraySized element (In takenMin takenMax)

A given number of elements in a given direction

import Linear exposing (Direction(..))
import N exposing (n7)


-- its three last elements
ArraySized.upTo n3AtLeast
    |> ArraySized.take Down { atLeast = n3 } n3

Is the amount taken less than the ArraySized's length minimum?

ArraySized.l8 0 1 2 3 4 5 6 7
    |> ArraySized.take Up { atLeast = n7 } n7
    --: ArraySized number_ (In (Up7 minX_) (Up7 maxX_))
    |> ArraySized.toList
--> [ 0, 1, 2, 3, 4, 5, 6 ]


ArraySized.l8 0 1 2 3 4 5 6 7
    |> ArraySized.take Up { atLeast = n7 } n7AtLeast
--: ArraySized number_ (Min (Up7 x_))


ArraySized.l8 0 1 2 3 4 5 6 7
    |> ArraySized.minTo
    |> ArraySized.take Up { atLeast = n2 } between2And7
--: ArraySized number_ (In (Up2 minX_) (Up7 maxX_))

Is the amount taken greater than the ArraySized's length minimum?

ArraySized.upTo between3And6
    -- its first four elements
    |> ArraySized.take Down { atLeast = n3 } (n4 |> N.minTo n3)

Open for alternative API suggestions!

Direction
-> N (In (Down maxPlusX To takenMaxPlusX) (Down min To takenMin))
-> ArraySized element (In (On min) (Up maxX To maxPlusX))
-> ArraySized
element
(In (On takenMin) (Up maxX To takenMaxPlusX))

Elements after a certain number of elements in a given Direction

import Linear exposing (Direction(..))
import N exposing (n2)


ArraySized.l4 0 1 2 3
    |> ArraySized.drop Down n2
    --: ArraySized number_ (In (Up2 minX_) (Up2 maxX_))
    |> ArraySized.toList
--> [ 0, 1 ]


between6And10Elements
    |> ArraySized.drop Up between2And3
    --: ArraySized number_ (In (Up3 minX_) (Up8 maxX_))
  • Don't know its length maximum? → dropMin
  • Can the dropped length's maximum be greater than its length's minimum? → isAtLeast, then drop
Direction
-> N (In (On droppedMin_) (Down min To takenMin))
-> ArraySized element (In (On min) max)
-> ArraySized element (In (On takenMin) max)

Elements after a certain number of elements in a given Direction

import Linear exposing (Direction(..))
import N exposing (n2)


atLeast6Elements
    |> ArraySized.dropMin Down n2
--: ArraySized ... (Min (On N4))
  • Know its length maximum? → drop
  • Can the dropped length's maximum be greater than its length's minimum? → isAtLeast, then drop
Direction
-> N
(In
(On (Add1 chunkMinFrom1))
(Up chunkMaxX To (Add1 chunkMaxMinus1PlusX))
)
-> ArraySized element (In (On minLength_) max)
-> { chunks :
ArraySized
(ArraySized
element
(In
(On (Add1 chunkMinFrom1))
(Up chunkMaxX To (Add1 chunkMaxMinus1PlusX))
)
)
(In (Up0 minX_) max)
, remainder :
ArraySized
element
(In
(Up remainderMinX To remainderMinX)
(Up chunkMaxX To chunkMaxMinus1PlusX)
)
}

Split the ArraySized into equal-sized (except remainder) slices in a given Direction

  • chunks: the ArraySized divided into equal-sized Arrs
  • remainder: values to one side that don't fill a whole group

↓

import Linear exposing (Direction(..))
import N exposing (n0, n5)


ArraySized.l7 1 2 3 4 5 6 7
    |> ArraySized.toChunksOf Up n5
    --: { chunks :
    --:     ArraySized
    --:         (ArraySized
    --:             number_
    --:             (In (Up5 chunkMinX_) (Up5 chunkMaxX))
    --:         )
    --:         (In (Up0 minX_) (Up7 maxX_))
    --: , remainder :
    --:     ArraySized
    --:         number_
    --:         (In
    --:             (Up0 remainderMinX_)
    --:             (Up4 chunkMaxX)
    --:         )
    --: }
    |> .remainder
    |> ArraySized.toList
--> [ 6, 7 ]




ArraySized.l7 1 2 3 4 5 6 7
    |> ArraySized.toChunksOf Down n5
    |> .remainder
    |> ArraySized.toList
--> [ 1, 2 ]

combine

and :
ArraySized nextElement range
-> ArraySized element range
-> ArraySized ( element, nextElement ) range

Combine each element with an element at the same index from a given ArraySized into a tuple

Every element beyond the minimum length of both is't part of the final ArraySized

import ArraySized


answer
    |> ArraySized.and guess
    |> ArraySized.map
        (\( answerChar, guessChar ) ->
            { directMatch = guessChar == answerChar }
        )

This is often misused

Direction
-> ArraySized
element
(In (Up minPlusX To minSumPlusX) (Up maxPlusX To maxSumPlusX))
-> ArraySized
element
(In (Up minX To minPlusX) (Up maxX To maxPlusX))
-> ArraySized
element
(In (Up minX To minSumPlusX) (Up maxX To maxSumPlusX))

Glue elements of an ArraySized with an amount of elements in a range to the end of a given direction

import Linear exposing (Direction(..))


ArraySized.l3 1 2 3
    |> ArraySized.attach Up (ArraySized.l3 4 5 6)
    --: ArraySized number_ (In (Up6 minX_) (Up6 maxX_))
    |> ArraySized.toList
--> [ 1, 2, 3, 4, 5, 6 ]


ArraySized.l3 1 2 3
    |> ArraySized.attach Down (ArraySized.l3 4 5 6)
    |> ArraySized.toList
--> [ 4, 5, 6, 1, 2, 3 ]

Don't know both length maxima? → attachMin

Direction
-> ArraySized
element
(In (Up minPlusX To minSumPlusX) extensionMax_)
-> ArraySized element (In (Up x To minPlusX) max_)
-> ArraySized element (Min (Up x To minSumPlusX))

Glue elements of an ArraySized to the end of a given direction

ArraySized.l3 1 2 3
    |> ArraySized.attachMin Up atLeast3Elements
--: ArraySized ... (Min (Up6 x_))


ArraySized.l3 1 2 3
    |> ArraySized.attachMin Down atLeast3Elements
--: ArraySized ... (Min (Up6 x_))

Know both length maxima? → attach

Direction
-> ( N (In (On paddingMin) (Up maxX To paddingMaxPlusX))
-> ArraySized
element
(In (On paddingMin) (Up maxX To paddingMaxPlusX))
)
-> N (In (On paddedMin) (Up maxX To paddedMaxPlusX))
-> ArraySized
element
(In
(Up paddingMaxPlusX To paddedMaxPlusX)
(Up paddingMin To paddedMin)
)
-> ArraySized
element
(In (On paddedMin) (Up maxX To paddedMaxPlusX))

Pad in a given Direction with a given ArraySized to reach a given length

import N exposing (n8)
import Linear exposing (Direction(..))


type Bit
    = I
    | O


ArraySized.l3 I O I
    |> ArraySized.padToLength Down (ArraySized.repeat O) n8
    --: ArraySized Bit (In (On N8) (Up8 x_))
    |> ArraySized.toList
--> [ O, O, O, O, O, I, O, I ]


ArraySized.l4
    (ArraySized.l3 I I I |> ArraySized.maxTo n8)
    (ArraySized.l8 O I I I O I O O)
    (ArraySized.l8 O I I I O I O O)
    (ArraySized.l8 O I I I O I O O)
    |> ArraySized.map
        (ArraySized.padToLength Down (ArraySized.repeat O) n8)
    |> ArraySized.map ArraySized.toList
    |> ArraySized.toList
--> [ [ O, O, O, O, O, I, I, I ]
--> , [ O, I, I, I, O, I, O, O ]
--> , [ O, I, I, I, O, I, O, O ]
--> , [ O, I, I, I, O, I, O, O ]
--> ]
ArraySized
element
(In (Up minPlusX To minSumPlusX) (Up maxPlusX To maxSumPlusX))
-> ArraySized element (In (Up x To minPlusX) (Up x To maxPlusX))
-> ArraySized
element
(In (Up x To minSumPlusX) (Up x To maxSumPlusX))

Place all elements of an ArraySized between all current members. Extra elements of either ArraySized are attached to the end without separating elements from the other ArraySized

import N exposing (n2)


ArraySized.l3 "turtles" "turtles" "turtles"
    |> ArraySized.interweave (ArraySized.repeat "on" n2)
    --: ArraySized String (In (Up5 minX_) (Up5 maxX_))
    |> ArraySized.toList
--> [ "turtles", "on", "turtles", "on", "turtles" ]


ArraySized.l3 "turtles" "turtles" "turtles"
    |> ArraySized.interweave (ArraySized.repeat "on" between5And10)
--→ "turtles" "on" "turtles" "on" "turtles" "on" "on" "on" ...
--: ArraySized String (In (Up5 minX_) (Up13 maxX_))

Don't know both maxima → interweaveMin

ArraySized
element
(In (Up minPlusX To minSumPlusX) interweaveMax_)
-> ArraySized element (In (Up x To minPlusX) max_)
-> ArraySized element (Min (Up x To minSumPlusX))

Place all elements of an ArraySized between all current members. Extra elements of either ArraySized are attached to the end without separating elements from the other ArraySized

import N exposing (n2)


ArraySized.l3 "turtles" "turtles" "turtles"
    |> ArraySized.interweaveMin
        (ArraySized.repeat "on" atLeast2)
    --: ArraySized String (Min (Up5 minX_))

Know both maxima → interweave

length compare

( N
(In
lowerLimitMin
(Up lowerLimitMaxX To (Add1 lowerLimitMaxPlusXMinus1))
)
, N (In (Up upperLimitMinX To upperLimitMinPlusX) upperLimitMax)
)
-> ArraySized element (In min max)
-> Result
(BelowOrAbove
(ArraySized
element
(In min (Up lowerLimitMaxX To lowerLimitMaxPlusXMinus1))
)
(ArraySized
element
(In (Up upperLimitMinX To (Add1 upperLimitMinPlusX)) max)
)
)
(ArraySized element (In lowerLimitMin upperLimitMax))

Compared to a range from a lower to an upper bound, is its length in, BelowOrAbove range?

import N exposing (n10, n16)


chooseFormation :
    ArraySized Character (In minLength_ N50)
    -> Formation
chooseFormation characters =
    case characters |> ArraySized.hasIn ( n10, n16 ) of
        Ok between10And16 ->
            SpecialAttack between10And16


        Err (N.Below n9AtMost) ->
            Retreat n9AtMost


        Err (N.Above n17AtLeast) ->
            Fight n17AtLeast
has :
N
(In
(Up minX To (Add1 comparedAgainstMinPlusXMinus1))
(Up maxX To (Add1 comparedAgainstMaxPlusXMinus1))
)
-> ArraySized element (In min max)
-> Result
(BelowOrAbove
(ArraySized
element
(In min (Up maxX To comparedAgainstMaxPlusXMinus1))
)
(ArraySized
element
(In (Up minX To (Add2 comparedAgainstMinPlusXMinus1)) max)
)
)
(ArraySized
element
(In
(Up minX To (Add1 comparedAgainstMinPlusXMinus1))
(Up maxX To (Add1 comparedAgainstMaxPlusXMinus1))
)
)

Compare its length to a given exact length. Does it match or is it BelowOrAbove?

import N exposing (n7)


chooseFormation :
    ArraySized Character (In min N50)
    -> Formation
chooseFormation characters =
    case characters |> ArraySized.has n7 of
        Ok exactly7 ->
            SpecialAttack exactly7


        Err (N.Below l6AtLeast) ->
            Retreat l6AtLeast


        Err (N.Above l8AtLeast) ->
            Fight l8AtLeast
N
(In
lowerLimitMin
(Up lowerLimitMaxX To (Add1 lowerLimitMaxMinus1PlusX))
)
-> ArraySized element (In min max)
-> Result
(ArraySized
element
(In min (Up lowerLimitMaxX To lowerLimitMaxMinus1PlusX))
)
(ArraySized element (In lowerLimitMin max))

Is its length below (Err) or at least as big as (Ok) a given N?

import N exposing (n5)


atLeast5 :
    ArraySized element (In minLength_ max)
    -> Maybe (element ArraySized (In (Up5 minX_) max))
atLeast5 =
    ArraySized.hasAtLeast n5
        >> Result.toMaybe
N (In (Up upperLimitMinX To upperLimitMinPlusX) upperLimitMax)
-> ArraySized element (In min max)
-> Result
(ArraySized
element
(In (Up upperLimitMinX To (Add1 upperLimitMinPlusX)) max)
)
(ArraySized element (In min upperLimitMax))

Is its length atMost (Ok) or above (Err) a given length?

-- at least 3 and only up to 50 tags
tag :
    ArraySized
        String
        (In (On (Add3 minFrom3_)) (Up maxTo50_ To N50))
    -> (Metadata -> MetadataTagged)


tagIfValidTags :
    ArraySized String (In (On (Add3 minFrom3_)) max_)
    -> (Metadata -> Maybe MetadataTagged)
tagIfValidTags tags =
    case tags |> ArraySized.hasAtMost n50 of
        Ok atMost50 ->
            tag atMost50 >> Just


        Err _ ->
            \_ -> Nothing

transform

map :
(element -> mappedElement)
-> ArraySized element range
-> ArraySized mappedElement range

Change all elements based on their current values

import N exposing (n25)


aToZ : ArraySized Char (In N26 (N26Plus a_))
aToZ =
    ArraySized.upTo n25
        |> ArraySized.map inABC


inABC index =
    ('a' |> Char.toCode)
        + (index |> N.toInt)
        |> Char.fromCode

Oh look, more type-safety!

result
-> Direction
-> (element -> result -> result)
-> ArraySized element lengthRange_
-> result

Reduce an ArraySized in a given Direction

import Linear exposing (Direction(..))


ArraySized.l4 'l' 'i' 'v' 'e'
    |> ArraySized.foldFrom "" Down String.cons
--> "live"


ArraySized.l4 'l' 'i' 'v' 'e'
    |> ArraySized.foldFrom "" Up String.cons
--> "evil"


sum =
    ArraySized.foldFrom 0 Up (\soFar n -> soFar + n)


product =
    ArraySized.foldFrom 0 Up (\soFar n -> soFar * n)
Direction
-> (element -> element -> element)
-> ArraySized element (In (On (Add1 minFrom1_)) max_)
-> element

A fold in a given Direction where the initial result is the first element in the ArraySized

import Linear exposing (Direction(..))


ArraySized.l3 234 345 543
    |> ArraySized.fold Up Basics.max
--> 543


ArraySized.l3 "go" "to" "uni"
    |> ArraySized.fold Down
        (\word soFar -> soFar ++ " " ++ word)
--> "uni to go"
(element -> accumulated)
-> Direction
-> (element -> accumulated -> accumulated)
-> ArraySized element (In (On (Add1 minFrom1_)) max_)
-> accumulated

Fold, starting from one end element transformed to the initial accumulation value, then reducing what's accumulated in a given Direction Usually used to convert to a different non-empty structure

import Emptiable exposing (Emptiable)
import N exposing (Add1, In, On)
import Stack exposing (Stacked)


toStackFilled :
    ArraySized
        element
        (In (On (Add1 minFrom1_)) max_)
    -> Emptiable (Stacked element) Never
toStackFilled =
    ArraySized.foldFromOne Stack.one Stack.onTopLay

fold is a simple version that folds directly from the start element:

Stack.fold =
    Stack.foldFromOne identity
toArray : ArraySized element lengthRange_ -> Array element

Convert to an Array.

Make these kinds of conversions your final step. Try to keep extra information as long as you can: "wrap early, unwrap late"

import N exposing (n4)
import Array


ArraySized.upTo n4
    |> ArraySized.map N.toInt
    |> ArraySized.toArray
--> Array.fromList [ 0, 1, 2, 3, 4 ]
toList : ArraySized element lengthRange_ -> List element

Convert to a List.

Make these kinds of conversions your final step. Try to keep extra information as long as you can: "wrap early, unwrap late"

import N exposing (n4)


ArraySized.upTo n4
    |> ArraySized.map N.toInt
    |> ArraySized.toList
--> [ 0, 1, 2, 3, 4 ]
ArraySized
element
(In
(On (N0OrAdd1 possiblyOrNever minFrom1_))
(Up maxTo1_ To N1)
)
-> Emptiable element possiblyOrNever

On empty Emptiable.empty, on one filled with its only value

Emptiness type information is transferred

ArraySized
element
(In (On (N0OrAdd1 possiblyOrNever minFrom1_)) max_)
-> Emptiable (Stacked element) possiblyOrNever

Convert to an Emptiable (Stacked ...) never_.

Make these kinds of conversions your final step. Try to keep extra information as long as you can: "wrap early, unwrap late"

import N exposing (n4)
import Emptiable
import Stack


ArraySized.upTo n4
    |> ArraySized.map N.toInt
    |> ArraySized.toStack
--> Stack.topBelow 0 [ 1, 2, 3, 4 ]
--: Emptiable (Stacked Int) Never


ArraySized.l4 (0 |> Emptiable.filled) Emptiable.empty Emptiable.empty (3 |> Emptiable.filled)
    |> ArraySized.fills
    |> ArraySized.toStack
--> Stack.topBelow 0 [ 3 ]
--: Emptiable (Stacked Int) Possibly
toString : ArraySized Char lengthRange_ -> String

Convert to a String.

Make these kinds of conversions your final step. Try to keep extra information as long as you can: "wrap early, unwrap late"

import N exposing (n4)


ArraySized.upTo n4
    |> ArraySized.map
        (\n ->
            ('a' |> Char.toCode)
                + (n |> N.toInt)
                |> Char.fromCode
        )
    |> ArraySized.toString
--> "abcde"

You have a use-case for mapAccumulate/mapAccumulateFrom? → issue/PR

to2 :
ArraySized
element
(In (On (Add2 minFrom2_)) (Up maxTo2_ To N2))
-> T2 element element

Transform into a Toop.T2 to simplify accessing elements, pattern matching

to3 :
ArraySized
element
(In (On (Add3 minFrom3_)) (Up maxTo3_ To N3))
-> T3 element element element

Transform into a Toop.T3 to simplify accessing elements, pattern matching

to4 :
ArraySized
element
(In (On (Add4 minFrom4_)) (Up maxTo4_ To N4))
-> T4 element element element element

Transform into a Toop.T4 to simplify accessing elements, pattern matching

to5 :
ArraySized
element
(In (On (Add5 minFrom5_)) (Up maxTo5_ To N5))
-> T5 element element element element element

Transform into a Toop.T5 to simplify accessing elements, pattern matching

to6 :
ArraySized
element
(In (On (Add6 minFrom6_)) (Up maxTo6_ To N6))
-> T6 element element element element element element

Transform into a Toop.T6 to simplify accessing elements, pattern matching

to7 :
ArraySized
element
(In (On (Add7 minFrom7_)) (Up maxTo7_ To N7))
-> T7 element element element element element element element

Transform into a Toop.T7 to simplify accessing elements, pattern matching

to8 :
ArraySized
element
(In (On (Add8 minFrom8_)) (Up maxTo8_ To N8))
-> T8
element
element
element
element
element
element
element
element

Transform into a Toop.T8 to simplify accessing elements, pattern matching

to9 :
ArraySized
element
(In (On (Add9 minFrom9_)) (Up maxTo9_ To N9))
-> T9
element
element
element
element
element
element
element
element
element

Transform into a Toop.T9 to simplify accessing elements, pattern matching

ArraySized
element
(In (On (Add10 minFrom10_)) (Up maxTo10_ To N10))
-> T10
element
element
element
element
element
element
element
element
element
element

Transform into a Toop.T10 to simplify accessing elements, pattern matching

ArraySized
element
(In (On (Add11 minFrom11_)) (Up maxTo11_ To N11))
-> T11
element
element
element
element
element
element
element
element
element
element
element

Transform into a Toop.T11 to simplify accessing elements, pattern matching

ArraySized
element
(In (On (Add12 minFrom2_)) (Up maxTo12_ To N12))
-> T12
element
element
element
element
element
element
element
element
element
element
element
element

Transform into a Toop.T12 to simplify accessing elements, pattern matching

ArraySized
element
(In (On (Add13 minFrom13_)) (Up maxTo13_ To N13))
-> T13
element
element
element
element
element
element
element
element
element
element
element
element
element

Transform into a Toop.T13 to simplify accessing elements, pattern matching

ArraySized
element
(In (On (Add14 minFrom14_)) (Up maxTo14_ To N14))
-> T14
element
element
element
element
element
element
element
element
element
element
element
element
element
element

Transform into a Toop.T14 to simplify accessing elements, pattern matching

ArraySized
element
(In (On (Add15 minFrom15_)) (Up maxTo15_ To N15))
-> T15
element
element
element
element
element
element
element
element
element
element
element
element
element
element
element

Transform into a Toop.T15 to simplify accessing elements, pattern matching

ArraySized
element
(In (On (Add16 minFrom16_)) (Up maxTo16_ To N16))
-> T16
element
element
element
element
element
element
element
element
element
element
element
element
element
element
element
element

Transform into a Toop.T16 to simplify accessing elements, pattern matching

You can generate to<x> with x >= 17 locally, put them in a module exposing (to<x>) + import as ArraySized

without internal functions

ArraySized element (In (On min) (On max))
-> ArraySized element (In min max)

ArraySized with a length of On range → equatable OnValue range

If you have a Min length, you instead only need minToNumber

ArraySized element (In min max)
-> ArraySized element (In (On min) (On max))

ArraySized with a length of equatable OnValue range → On range, allowing it to be altered, compared, ...

If you have a Min length, you instead only need minToOn

ArraySized element (In (On min) max)
-> ArraySized element (In min max)

ArraySized with a length with an On minimum → equatable OnValue minimum

You'll usually use this to convert to a Min (OnValue ...) length

ArraySized element (In min max)
-> ArraySized element (In (On min) max)

ArraySized with a length with an equatable OnValue minimum → On minimum, allowing it to be altered, compared, ...

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

ArraySized element (In min (On max))
-> ArraySized element (In min max)

ArraySized with a length with an On maximum → equatable OnValue maximum

ArraySized element (In min max)
-> ArraySized element (In min (On max))

ArraySized with a length with an equatable OnValue maximum → On maximum, allowing it to be altered, compared, ...

type-level

N (In minNew (Up minNewMaxToMin_ To min))
-> ArraySized element (In (On min) max)
-> ArraySized element (In minNew max)

Make an ArraySized with a on maximum length fit into functions with require a higher maximum length

type alias Row =
    ArraySized Field (Exactly (On N18))

Row's length range can't be added to another length

attach2TemporaryFields : Row -> ...
attach2TemporaryFields rowFromModelOrSomeStorage =
    ArraySized.repeat Temporary n2
        |> ArraySized.attach Up rowFromModelOrSomeStorage

Only Up<n> x can do that

attach2TemporaryFields :
    Row
    ->
        ArraySized
            Field
            (In (Up20 minX_) (Up20 maxX_))
attach2TemporaryFields rowFromModelOrSomeStorage =
    ArraySized.repeat Temporary n2
        |> ArraySized.attach Up
            (rowFromModelOrSomeStorage
                |> ArraySized.minTo n18
                |> ArraySized.maxTo n18
            )
N (In maxDecreaseMin_ (Down minPlusX To minDecreasedPlusX))
-> ArraySized element (In (Up x To minPlusX) max)
-> ArraySized element (In (Up x To minDecreasedPlusX) max)

Have a specific minimum in mind? → minTo

Want to increase the upper bound by a on amount? ↓

ArraySized.l4 'a' 'b' 'c' 'd'
    --: ArraySized Char (In (Up4 minX_) (Up4 maxX_))
    |> ArraySized.minSubtract n2
--: ArraySized Char (In (Up2 minX_) (Up4 maxX_))

When is this useful? Very rarely, to preserve type variables

emptiablePush :
    Emptiable element possiblyOrNever_
    ->
        (ArraySized
            element
            (In
                (Up minX To minPlusX)
                (Up maxX To maxPlusX)
            )
         ->
            ArraySized
                element
                (In (Up minX To minPlusX) (Up maxX To (Add1 maxPlusX)))
        )
emptiablePush emptiableElementToPush =
    case emptiableElementToPush of
        Emptiable.Empty _ ->
            ArraySized.maxAdd n1


        Emptiable.Filled elementToPush ->
            ArraySized.push elementToPush
                >> ArraySized.minSubtract n1

Here, you could alternatively attach its fromEmptiable.

More in N.minSubtract

N (In (On maxNewMin) maxNew)
-> ArraySized element (In min (Up maxToMaxNewMin_ To maxNewMin))
-> ArraySized element (In min maxNew)

Make an ArraySized with a on maximum length fit into functions with require a higher maximum length

type alias Row =
    ArraySized Field (Exactly (On N18))

Row's length range can't be added to another length

attach2TemporaryFields : Row -> ...
attach2TemporaryFields rowFromModelOrSomeStorage =
    ArraySized.repeat Temporary n2
        |> ArraySized.attach Up rowFromModelOrSomeStorage

Only Up<n> x can do that

attach2TemporaryFields :
    Row
    ->
        ArraySized
            Field
            (In (Up20 minX_) (Up20 maxX_))
attach2TemporaryFields rowFromModelOrSomeStorage =
    ArraySized.repeat Temporary n2
        |> ArraySized.attach Up
            (rowFromModelOrSomeStorage
                |> ArraySized.minTo n18
                |> ArraySized.maxTo n18
            )

Another example: re-enabling an argument's maximum difference

atMost18Elements : ArraySized ... (In min_ (Up maxTo18_ To N18))

The argument in atMost18Elements should also fit in atMost19Elements for example

atMost19Elements theArgument -- error


atMost19Elements (theArgument |> ArraySized.maxTo n19)

maxAdd n1 is also possible, but unless you want to preserve the maxTo18_ type variable, there's no need to not use this absolute operation

maxToInfinity : ArraySized element (In min max_) -> ArraySized element (Min min)

Convert the ArraySized (In min ...) to a ArraySized (Min min)

between4And10Elements |> ArraySized.maxToInfinity
--: ArraySized ... (Min (Up4 x_))

There is only 1 situation you should use this

To make these the same type

[ atLeast1Element, between1And10Elements ]

Elm complains

all the previous elements in the list are ArraySized ... (Min N1)

[ atLeast1Element
, between1And10Elements |> ArraySized.maxToInfinity
]
N (In maxIncreaseMin_ (Up maxPlusX To maxIncreasedPlusX))
-> ArraySized element (In min (Up x To maxPlusX))
-> ArraySized element (In min (Up x To maxIncreasedPlusX))

Have a specific maximum in mind? → maxTo

Want to increase the upper bound by a on amount? ↓

ArraySized.l4 'a' 'b' 'c' 'd'
    --: ArraySized Char (In (Up4 minX_) (Up4 maxX_))
    |> ArraySized.maxAdd n2
--: ArraySized Char (In (Up4 minX_) (Up6 maxX_))

When is this useful? Very rarely, to preserve type variables

emptiablePush :
    Emptiable element possiblyOrNever_
    ->
        (ArraySized
            element
            (In
                (Up minX To minPlusX)
                (Up maxX To maxPlusX)
            )
         ->
            ArraySized
                element
                (In (Up minX To minPlusX) (Up maxX To (Add1 maxPlusX)))
        )
emptiablePush emptiableElementToPush =
    case emptiableElementToPush of
        Emptiable.Empty _ ->
            ArraySized.maxAdd n1


        Emptiable.Filled elementToPush ->
            ArraySized.push elementToPush
                >> ArraySized.minSubtract n1

Here, you could alternatively attach its fromEmptiable.

More in N.maxAdd

advanced: generic allowable-state

ArraySized
element
(In (On (N0OrAdd1 possiblyOrNever minFrom1)) max)
-> Emptiable
(ArraySized element (In (Up1 minX_) max))
possiblyOrNever

Transfer the knowledge about whether n0 is a possible length

toEmptiable :
    ArraySized
        element
        (In (On (N0OrAdd1 possiblyOrNever minFrom1_)) (Up maxTo1_ To N1))
    -> Emptiable element possiblyOrNever
toEmptiable =
    \arraySized ->
        arraySized
            |> ArraySized.hasAtLeast1
            |> Emptiable.map ArraySized.toOne

It really is just that simple: first try hasAtLeast1, then map that possibility using the knowledge that the ArraySized has at least 1 element

toStack :
    ArraySized
        element
        (In (On (N0OrAdd1 possiblyOrNever minFrom1_)) max_)
    -> Emptiable (Stacked element) possiblyOrNever
toStack =
    \arraySized ->
        arraySized
            |> ArraySized.hasAtLeast1
            |> Emptiable.mapFlat
                (ArraySized.foldFromOne Stack.one Down Stack.onTopLay)
(possiblyOrNever -> adaptedPossiblyOrNever)
-> ArraySized
element
(In (On (N0OrAdd1 possiblyOrNever minFrom1)) max)
-> ArraySized
element
(In (On (N0OrAdd1 adaptedPossiblyOrNever minFrom1)) max)

Change the possiblyOrNever type for the case that its length minimum is 0

never allows you to adapt any variable, \_ -> yourVariablePossiblyOrNever swaps it for your given variable

fromEmptiable :
    Emptiable element possiblyOrNever
    ->
        ArraySized
            element
            (In (On (N0OrAdd1 possiblyOrNever N0)) (Up1 maxX_))
fromEmptiable =
    \emptiable ->
        case emptiable of
            Emptiable.Filled content ->
                ArraySized.one content
                    -- min = 0 can never happen → any variable possible
                    |> ArraySized.min0Adapt never


            Emptiable.Empty possiblyOrNever ->
                ArraySized.empty
                    |> ArraySized.min0Adapt (\_ -> possiblyOrNever)
                    -- there's no min successor → any variable possible
                    |> ArraySized.minAtLeast1Never
                    -- the other has max = 1. Let's adapt that higher max here
                    |> ArraySized.maxAdd n1

using minAtLeast1Never, maxAdd

Read more at N.min0Adapt

ArraySized
element
(In (On (N0OrAdd1 possiblyOrNever Never)) max)
-> ArraySized
element
(In (On (N0OrAdd1 possiblyOrNever minFrom1_)) max)

Change the successor type for the case that its length min is 1 + ... to allow adapting any variable

fromEmptiable :
    Emptiable element possiblyOrNever
    ->
        ArraySized
            element
            (In (On (N0OrAdd1 possiblyOrNever N0)) (Up1 maxX_))
fromEmptiable =
    \emptiable ->
        case emptiable of
            Emptiable.Filled content ->
                ArraySized.one content
                    -- min = 0 can never happen → any variable possible
                    |> ArraySized.min0Adapt never


            Emptiable.Empty possiblyOrNever ->
                ArraySized.empty
                    |> ArraySized.min0Adapt (\_ -> possiblyOrNever)
                    -- there's no min successor → any variable possible
                    |> ArraySized.minAtLeast1Never
                    -- the other has max = 1. Let's adapt that higher max here
                    |> ArraySized.maxAdd n1

using isAtLeast1, min0Adapt.