lue-bird/elm-typesafe-array/33.2.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 ... (In (On N4) (On N15))


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

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

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 (Up minX To minPlusX) max)
-> ArraySized
(N (In (Up1 nMinX_) max))
(In (Up minX To minPlusX) max)

Increasing natural numbers from n1 until including a given number

import N exposing (n3, n0)


ArraySized.n1To n3
--: ArraySized
--:     (N (In (Up1 nMinX_) (Up3 maxX)))
--:     (In (Up3 minX_) (Up3 maxX))
    |> ArraySized.map N.toInt
    |> ArraySized.toList
--> [ 1, 2, 3 ]


ArraySized.n1To n0
--: ArraySized
--:     (N (In (Up1 nMinX_) (Up0 maxX)))
--:     (In (Up0 minX_) (Up0 maxX))
    |> ArraySized.map N.toInt
    |> ArraySized.toList
--> []
-- This does look weird at first glance...
-- The ArraySized says it has Ns in it that are impossible to construct!
-- but I promise it makes sense:
-- The ArraySized is empty, so we're all good after all


ArraySized.n1To between2And9
    |> ArraySized.map (N.add n3)
--: ArraySized
--:    (N (In (Up5 nMinX_) (Up12 maxX)))
--:    (In (Up3 minX_) (Up10 maxX))

To add index info to your ArraySized, use andIndexes

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 well with

Random.andThen
    (ArraySized.random <element>)
    (N.randomIn ( <length min>, <length max> ))
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.n1To 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.n1To 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.n1To n6 -- big yes

"wrap early, unwrap late"

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

observe

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 (On (Add1 indexMin_)) (Up indexMaxToMin_ To min))
)
-> ArraySized element (In (On min) max_)
-> element

Its element at a valid location in a given Direction

Remember that indexes are 1-indexed!

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


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


ArraySized.l4 0 1 2 3
    |> ArraySized.element ( Down, n2 )
--> 2
( Direction, N indexRange_ )
-> ArraySized element range_
-> 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 3
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

observe length

( N
(In
lowerLimitMin
(Up lowerLimitMaxX To (Add1 lowerLimitMaxPlusXFrom1))
)
, N (In (Up upperLimitMinX To upperLimitMinPlusX) upperLimitMax)
)
-> ArraySized element (In min max)
-> Result
(BelowOrAbove
(ArraySized
element
(In min (Up lowerLimitMaxX To lowerLimitMaxPlusXFrom1))
)
(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 comparedAgainstMinPlusXFrom1))
(Up maxX To (Add1 comparedAgainstMaxPlusXFrom1))
)
-> ArraySized element (In min max)
-> Result
(BelowOrAbove
(ArraySized
element
(In min (Up maxX To comparedAgainstMaxPlusXFrom1))
)
(ArraySized
element
(In (Up minX To (Add2 comparedAgainstMinPlusXFrom1)) max)
)
)
(ArraySized
element
(In
(Up minX To (Add1 comparedAgainstMinPlusXFrom1))
(Up maxX To (Add1 comparedAgainstMaxPlusXFrom1))
)
)

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 lowerLimitMaxFrom1PlusX))
)
-> ArraySized element (In min max)
-> Result
(ArraySized
element
(In min (Up lowerLimitMaxX To lowerLimitMaxFrom1PlusX))
)
(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

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" ]
ArraySized element (In (Up minX To minPlusX) max)
-> ArraySized
{ element : element
, index : N (In (Up1 nMinX_) max)
}
(In (Up minX To minPlusX) max)

Add index info to each element.

import N exposing (n1, n2, n3)


ArraySized.l3 'a' 'b' 'c'
    |> ArraySized.andIndexes
    |> ArraySized.toList
--→ [ { element = 'a', index = n1 |> N.maxTo n3 }
--→ , { element = 'b', index = n2 |> N.minTo n1 |> N.maxTo n3 }
--→ , { element = 'b', index = n3 |> N.minTo n1 }
--→ ]
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.n1To n25
        |> ArraySized.map inABC


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

Oh look, more type-safety!

folded
-> Direction
-> ( { element : element
, folded : folded
}
-> { element : mappedElement
, folded : folded
}
)
-> ArraySized element range
-> { mapped : ArraySized mappedElement range
, folded : folded
}

Map each element using information collected from previous steps, folding in a given Direction from given initial information.

Both the mapped ArraySized and the folded information will be returned

You'll often find this under the name "mapAccum"

import Linear exposing (Direction(..))


ArraySized.l3 1 2 3
    |> ArraySized.mapFoldFrom 0
        Down
        (\state ->
            { element = state.folded
            , folded = state.folded + state.element
            }
        )
--→ { mapped = ArraySized.l3 5 3 0, folded = 6 }


mapIndexed : Direction -> (Int -> a -> b) -> (ArraySized a l -> ArraySized b l)
mapIndexed indexDirection mapAtIndex =
    ArraySized.mapFoldFrom 0
        indexDirection
        (\state ->
            { element = state.element |> mapAtIndex state.folded
            , folded = state.folded + 1
            }
        )
        >> .mapped


ArraySized.l4 'h' 'i' 'y' 'o'
    |> mapIndexed Up Tuple.pair
--→ ArraySized.l4 ( 0, 'h' ) ( 1, 'i' ) ( 2, 'y' ) ( 3, 'o' )


ArraySized.l4 'h' 'i' 'y' 'o'
    |> mapIndexed Down Tuple.pair
--→ ArraySized.l4 ( 3, 'h' ) ( 2, 'i' ) ( 1, 'y' ) ( 0, 'o' )
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) max_)
-> 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
(On (Add1 indexMinFrom1_))
(Up indexMaxToMin_ To (Add1 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

Remember that indexes are 1-indexed!

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


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


ArraySized.l3 'a' 'c' 'd'
    |> ArraySized.insert ( Down, n3 ) '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
(On (Add1 indexMinFrom1_))
(Up indexMaxToMin_ To (Add1 min))
)
)
-> element
-> ArraySized element (In (On min) max_)
-> 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
(On (Add1 indexMinFrom1_))
(Up indexMaxToMinFrom1_ To (Add1 minFrom1))
)
)
-> ArraySized
element
(In (On (Add1 minFrom1)) (Up maxX To (Add1 maxFrom1PlusX)))
-> ArraySized
element
(In (On minFrom1) (Up maxX To maxFrom1PlusX))

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

Remember that indexes are 1-indexed!

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


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

Kick out the element at an index in a given Direction

Remember that indexes are 1-indexed!

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 fill possiblyOrNever_) (In min_ max)
-> ArraySized fill (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" ]

Use 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 fill possiblyOrNever) range
-> Emptiable (ArraySized fill 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
ArraySized (Result error ok) range
-> Result (Emptiable (Stacked error) Never) (ArraySized ok range)

If every Result is Ok, all of the values. If any element is Err, all the errors.

import Stack


ArraySized.empty
    |> ArraySized.allOk
    |> Result.map ArraySized.toList
--> Ok []


ArraySized.l3 (Ok 1) (Ok 2) (Ok 3)
    |> ArraySized.allOk
    |> Result.map ArraySized.toList
--> Ok [ 1, 2, 3 ]


ArraySized.l3 (Ok 1) (Err "not a number") (Ok 3)
    |> ArraySized.allOk
--> Err (Stack.one "not a number")

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.n1To 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.n1To between3And6
    -- its first four elements
    |> ArraySized.take Down { atLeast = n3 } (n4 |> N.minTo n3)

If you're having trouble understanding the atLeast field, think of looking at the implementation of take will help:

take direction { atLeast } toTakeAmount =
    minTo atLeast
        >> takeCurrentMin direction toTakeAmount

Open for alternative API suggestions! I'm actually close to removing take in favor of takeCurrentMin and renaming that to take. Let me know what you think!

Direction
-> N (In min takenMax)
-> ArraySized element (In min max_)
-> ArraySized element (In min takenMax)

Take a given number of elements in a given direction

As the simpler version of take you can only take at least the type's minimum amount.

It's most of the time not what you want, since you usually want a shorter array minimum after taking.

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? → dropMax
Direction
-> N (In 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? → dropMax
Direction
-> N (In (Down max To differenceMax) toDropMax_)
-> ArraySized element (In min_ (On max))
-> ArraySized element (In (Up0 minX_) (On differenceMax))

Elements after a certain number of elements in a given Direction

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


ArraySized.l4 0 1 2 3
    |> ArraySized.dropMax Down (3 |> N.intToIn ( n2, n5 ))
    --: ArraySized number_ (In (Up0 minX_) (On N2))
    |> ArraySized.toList
--> [ 0, 1 ]
  • Is the dropped length's maximum less than its length's minimum? → drop
  • Don't know the array's length maximum? → dropMin
Direction
-> N (In (Up newMinX To newMinPlusX) newMax)
-> (N (In (Up1 indexMin_) newMax) -> element)
-> ArraySized element range_
-> ArraySized element (In (Up newMinX To newMinPlusX) newMax)

Reach a given length:

  • If the current length is greater than a given length, take the new length in a given direction

  • If the current length is less than a given length, pad further in a given Direction based on the index in the final ArraySized

.

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


type Bit
    = I
    | O


ArraySized.l3 I O I
    |> ArraySized.toSize Down n8 (\_ -> O)
    --: 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.toSize Down n8 (\_ -> O))
    |> 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 ]
--> ]
Direction
-> N
(In
(On (Add1 chunkMinFrom1))
(Up chunkMaxX To (Add1 chunkMaxFrom1PlusX))
)
-> ArraySized element (In minLength_ max)
-> { chunks :
ArraySized
(ArraySized
element
(In
(On (Add1 chunkMinFrom1))
(Up chunkMaxX To (Add1 chunkMaxFrom1PlusX))
)
)
(In (Up0 minX_) max)
, remainder :
ArraySized
element
(In
(Up remainderMinX To remainderMinX)
(Up chunkMaxX To chunkMaxFrom1PlusX)
)
}

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

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

transform

result
-> Direction
-> (element -> result -> result)
-> ArraySized element range_
-> 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 -> folded)
-> Direction
-> (element -> folded -> folded)
-> ArraySized element (In (On (Add1 minFrom1_)) max_)
-> folded

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 range_ -> 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.n1To n4
    |> ArraySized.map N.toInt
    |> ArraySized.toArray
--> Array.fromList [ 1, 2, 3, 4 ]
toList : ArraySized element range_ -> 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.n1To n4
    |> ArraySized.map N.toInt
    |> ArraySized.toList
--> [ 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.n1To n4
    |> ArraySized.map N.toInt
    |> ArraySized.toStack
--> Stack.topBelow 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 range_ -> 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.n1To n4
    |> ArraySized.map
        (\n ->
            ('a' |> Char.toCode)
                + (n |> N.toInt) - 1
                |> Char.fromCode
        )
    |> ArraySized.toString
--> "abcd"

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
            )
ArraySized element (In min_ max)
-> ArraySized element (In (Up0 minX_) max)

Equivalent to minTo n0, except that the current length minimum isn't required to be On.

It can be used to make argument types to functions look nicer.

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
(Down minX To minXDecreased)
(Down minPlusX To minPlusXDecreased)
)
-> ArraySized element (In (Up minX To minPlusX) max)
-> ArraySized
element
(In (Up minXDecreased To minPlusXDecreased) max)

Decrease the start and end of its length minimum difference

ArraySized.repeat () n3
    --: ArraySized () (In (Up3 (Add2 minX_)) (Up3 maxX_))
    |> ArraySized.minEndsSubtract n2
--: ArraySized () (In (Up5 minX_) (Up5 maxX_))

N.maxEndsSubtract has an example of where this can be useful.

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

N
(In
(Down maxPlusX To maxPlusXDecreased)
(Down maxX To maxXDecreased)
)
-> ArraySized element (In min (Up maxX To maxPlusX))
-> ArraySized
element
(In min (Up maxXDecreased To maxPlusXDecreased))

Decrease the start and end of its length maximum difference

ArraySized.repeat () n3
    --: ArraySized () (In (Up3 minX_) (Up3 (Add2 maxX_)))
    |> ArraySized.maxEndsSubtract n2
--: ArraySized () (In (Up5 minX_) (Up5 maxX_))

N.maxEndsSubtract has an example of where this can be useful.

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 0 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 minimum 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 hasAtLeast1, min0Adapt.