lue-bird/elm-typesafe-array/25.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 (In (Add1 minMinus1_) max_) comparable
    -> 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 lengthRange element =
ArraySized lengthRange element

An Array that knows about the amount of elements it holds

result type

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


-- 2 <= amount <= 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: glueing, taking, dropping, chunking, comparing, ...

argument type

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


-- amount >= 4
: ArraySized (In (Fixed (Add4 minMinus4_)) max_) ...


-- 4 <= amount <= 15
: ArraySized
:     (In (Fixed (Add4 minMinus4_)) (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 Fixed N<n>, avoiding type variables

-- amount >= 4
: ArraySized (Min (Fixed N4)) ...


-- 4 <= amount <= 15
: ArraySized (InFixed N4 N15) ...


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

InFixed min max being a shorthand for

In (Fixed min) (Fixed max)

Exactly n being a shorthand for

InFixed n n

== on ranges crashes elm. Use safe comparison methods or convert toValue

create

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

A given amount of same elements

import N exposing (n4)


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


ArraySized.repeat 'L' atLeast3
--: ArraySized (Min (Up3 x_)) Char
Generator element
-> N range
-> Generator (ArraySized range element)

Random.Generator for a given amount of random elements

import N exposing (n5)


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

Pairs really well with

N.randomIn ( <length min>, <length max> )
    |> Random.andThen
        (ArraySized.random <element>)
N (In (Fixed min) (Up maxX To maxPlusX))
-> ArraySized
(In (Fixed (Add1 min)) (Up maxX To (Add1 maxPlusX)))
(N (In (Up0 minX_) (Up maxX To maxPlusX)))

Increasing natural numbers until including a given number

import N exposing (n3)


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


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

minTo is helpful to turn the Fixed length minimum into a difference if you need that (for results etc.)

fromArray : Array element -> ArraySized (Min (Up0 x_)) element

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 (Min (Up0 x_)) element

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 (In (Up0 minX_) (Up1 maxX_)) element

On Just ArraySized.l1, on Nothing 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
--> []

Sadly, they way natural number constraints are defined, possiblyOrNever can't be transferred

Emptiable (Stacked element) Never
-> ArraySized (Min (Up1 x_)) element

Create from a Stack. As every Stack has >= 0 elements:

listFromSomeOtherLibrary |> ArraySized.fromStackFilled
--: ArraySized (Min (Up1 x_))

Don't use for construction:

ArraySized.fromStackFilled
    (Stack.topDown 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"

Only have an Emptiable (Stacked ...) Possibly? → fromStackEmptiable

Emptiable (Stacked element) possiblyOrNever_
-> ArraySized (Min (Up0 x_)) element

Create from a Stack. As every Stack has >= 0 elements:

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

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"

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

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

Create from a String. As every String has >= 0 elements:

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

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

specific length

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

No elements

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

Create with only 1 single given element

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

Create with 2 given elements in the order they are supplied

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

Create with 3 given elements in the order they are supplied

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

Create with 4 given elements in the order they are supplied

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

Create with 5 given elements in the order they are supplied

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

Create with 6 given elements in the order they are supplied

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

Create with 7 given elements in the order they are supplied

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

Create with 8 given elements in the order they are supplied

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

Create with 9 given elements in the order they are supplied

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

Create with 10 given elements in the order they are supplied

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

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 (In (Up12 minX_) (Up12 maxX_)) element

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 (In (Up13 minX_) (Up13 maxX_)) element

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 (In (Up14 minX_) (Up14 maxX_)) element

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 (In (Up15 minX_) (Up15 maxX_)) element

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 (In (Up16 minX_) (Up16 maxX_)) element

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 range element_ -> 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 indexMaxToMinMinus1_ To minMinus1))
)
-> ArraySized (In (Fixed (Add1 minMinus1)) max_) element
-> 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 lengthRange_ element
-> Result { indexBeyondElements : () } element

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

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


ArraySized.l4 0 1 2 3
    |> ArraySized.elementTry ( Up, n5 )
--> Err { indexBeyondElements = () }


ArraySized.l4 0 1 2 3
    |> ArraySized.elementTry ( Down, n1 )
--> Ok 2
to1 : ArraySized (Exactly N1) element -> element

Its only value

ArraySized.l1 "hi" |> ArraySized.to1
--> "hi"
all : (element -> Bool) -> ArraySized lengthRange_ element -> Bool

Whether all elements satisfy a given test

ArraySized.l2 2 3 |> ArraySized.all (\n -> n <= 4)
--> True


ArraySized.l2 2 7 |> ArraySized.all (\n -> n <= 4)
--> False


ArraySized.empty |> ArraySized.all (\n -> n <= 4)
--> True
any : (element -> Bool) -> ArraySized lengthRange_ element -> Bool

Whether at least one element satisfies a given test

ArraySized.l2 300 -5 |> ArraySized.any (\n -> n <= 4)
--> True


ArraySized.l2 5 5 |> ArraySized.any (\n -> n <= 4)
--> False


ArraySized.empty |> ArraySized.any (\n -> n <= 4)
--> False

alter

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

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 range element
-> ArraySized range element

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 range element -> ArraySized range element

Flip the order of the elements

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

Put a new element after all the others

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

pushMin if you don't know the length maximum

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

Put a new element after all the others

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

push if you know the length maximum

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

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
    --:     (In (Fixed N4) (Up4 maxX_))
    --:     Char
    |> 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 Fixed (for results etc.) → |> minTo

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

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 (Min (Fixed N6)) String


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

insert if you know the length maximum

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

( Direction
, N (In indexMin_ (Up indexMaxToMinMinus1_ To minMinus1))
)
-> ArraySized
(In
(Fixed (Add1 minMinus1))
(Up maxX To (Add1 maxMinus1PlusX))
)
element
-> ArraySized
(In (Fixed minMinus1) (Up maxX To maxMinus1PlusX))
element

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

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


removeLast between1And10Elements =
    between1And10Elements
        |> ArraySized.elementRemove ( Down, n0 )

Don't know the length maximum? → elementRemoveMin

( Direction
, N (In indexMin_ (Up indexMaxToMinMinus1_ To minMinus1))
)
-> ArraySized
(In (Fixed (Add1 minMinus1)) (Up x To maxPlusX))
element
-> ArraySized (In (Fixed minMinus1) (Up x To maxPlusX)) element

Kick out the element at an index in a given Direction

removeLast =
    TypeSized.elementRemoveMin ( Down, n0 )

Know the length maximum? → elementRemoveMin

filter

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

Take every filled value, drop every empty

import Emptiable exposing (filled)


ArraySized.l3 (filled "This") Emptiable.empty (filled "fine")
    |> ArraySized.fills
    --: ArraySized
    --:     (In (Up0 minX_) (Up3 maxX_))
    --:     String
    |> 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 range (Emptiable value possiblyOrNever)
-> Emptiable (ArraySized range value) possiblyOrNever

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

import Emptiable exposing (filled, fillMap)


ArraySized.empty
    |> ArraySized.allFill
    |> fillMap ArraySized.toList
--> filled []


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


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

Funnily, this 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, N (In min takenMax) )
-> ArraySized (In min max_) element
-> ArraySized (In min takenMax) element

A given number of elements to a given direction

It's easy if the minimum to take and the ArraySized's minimum length match

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


-- its three last elements
ArraySized.upTo n3AtLeast
    |> ArraySized.take ( Down, n3 )

If the amount taken is greater than the ArraySized's length minimum, add |> N.minTo currentLengthMinimum

-- its first four elements
ArraySized.upTo between3And6
    |> ArraySized.take ( Down, n4 |> N.minTo n3 )

If the amount taken is less than the ArraySized's length minimum, add |> ArraySized.minTo takenMinimum

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


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


ArraySized.l8 0 1 2 3 4 5 6 7
    |> ArraySized.minTo n2
    |> ArraySized.take ( Up, between2And7 )
--: ArraySized
--:     (In (Up2 minX_) (Up7 maxX_))
--:     number_
( Direction
, N (In (Down maxPlusX To takenMaxPlusX) (Down min To takenMin))
)
-> ArraySized (In (Fixed min) (Up maxX To maxPlusX)) element
-> ArraySized
(In (Fixed takenMin) (Up maxX To takenMaxPlusX))
element

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
    --:     (In (Up2 minX_) (Up2 maxX_))
    --:     number_
    |> ArraySized.toList
--> [ 0, 1 ]


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

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 (Fixed N4)) ...

Know its length maximum? → drop

( Direction, N (In (Down max To takenMax) takenMax_) )
-> ArraySized (In min_ (Fixed max)) element
-> ArraySized
(In (Up resultMinX To resultMinX) (Fixed takenMax))
element

drop a given length that can be greater than the ArraySized's length maximum

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


between3And6Elements
    |> ArraySized.dropOverMin ( Down, n5 )
--: ArraySized (In (Up0 minX_) (Fixed N1)) ...
Direction
-> N
(In
(Fixed (Add1 chunkMinMinus1))
(Up chunkMaxX To (Add1 chunkMaxMinus1PlusX))
)
-> ArraySized (In minLength_ max) element
-> { chunks :
ArraySized
(In (Up0 minX_) max)
(ArraySized
(In
(Fixed (Add1 chunkMinMinus1))
(Up chunkMaxX To (Add1 chunkMaxMinus1PlusX))
)
element
)
, remainder :
ArraySized
(In
(Up remainderMinX To remainderMinX)
(Up chunkMaxX To chunkMaxMinus1PlusX)
)
element
}

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
    --:         (In (Up0 minX_) (Up7 maxX_))
    --:         (ArraySized
    --:             (In (Up5 chunkMinX_) (Up5 chunkMaxX))
    --:             number_
    --:         )
    --: , remainder :
    --:     ArraySized
    --:         (In
    --:             (Up0 remainderMinX_)
    --:             (Up4 chunkMaxX)
    --:         )
    --:         number_
    --: }
    |> .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 length nextElement
-> ArraySized length element
-> ArraySized length ( element, nextElement )

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
(In (Up minPlusX To minSumPlusX) (Up maxPlusX To maxSumPlusX))
element
-> ArraySized
(In (Up minX To minPlusX) (Up maxX To maxPlusX))
element
-> ArraySized
(In (Up minX To minSumPlusX) (Up maxX To maxSumPlusX))
element

Attach elements of an ArraySized with an exact amount of elements to a given direction

import Linear exposing (Direction(..))


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


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

Don't know both length maxima? → glueMin

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

Attach elements of an ArraySized to the end in a given direction

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


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

Know both length maxima? → glue

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

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 (In (Fixed 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
(In (Up minPlusX To minSumPlusX) (Up maxPlusX To maxSumPlusX))
element
-> ArraySized (In (Up x To minPlusX) (Up x To maxPlusX)) element
-> ArraySized
(In (Up x To minSumPlusX) (Up x To maxSumPlusX))
element

Place all elements of an ArraySized between all current members. Extra elements of either ArraySized are glued 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
    --:     (In (Up5 minX_) (Up5 maxX_))
    --:     String
    |> 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
--:     (In (Up5 minX_) (Up13 maxX_))
--:     String

Don't know both maxima → interweaveMin

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

Place all elements of an ArraySized between all current members. Extra elements of either ArraySized are glued 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 (Min (Up5 minX_)) String

Know both maxima → interweave

length compare

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

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

import N exposing (n10, n16)


chooseFormation :
    ArraySized (In minLength_ N50) Character
    -> 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 (In min max) element
-> Result
(BelowOrAbove
(ArraySized
(In min (Up maxX To comparedAgainstMaxPlusXMinus1))
element
)
(ArraySized
(In (Up minX To (Add2 comparedAgainstMinPlusXMinus1)) max)
element
)
)
(ArraySized
(In
(Up minX To (Add1 comparedAgainstMinPlusXMinus1))
(Up maxX To (Add1 comparedAgainstMaxPlusXMinus1))
)
element
)

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

import N exposing (n7)


chooseFormation :
    ArraySized (In min N50) Character
    -> 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 (In min max) element
-> Result
(ArraySized
(In min (Up lowerLimitMaxX To lowerLimitMaxMinus1PlusX))
element
)
(ArraySized (In lowerLimitMin max) element)

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

import N exposing (n5)


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

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

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


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


        Err _ ->
            \_ -> Nothing

transform

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

Change all elements based on their current values

import N exposing (n25)


aToZ : ArraySized (In N26 (N26Plus a_)) Char
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 lengthRange_ element
-> 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 (In (Fixed (Add1 minMinus1_)) max_) element
-> 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"
toArray : ArraySized lengthRange_ element -> 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 lengthRange_ element -> 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 (In min_ (Up maxToN1_ To N1)) element
-> Emptiable element Possibly

On empty Nothing, on l1 Just it's only value

Sadly, they way natural number constraints are defined, emptiness type information can't be transferred

ArraySized lengthRange_ element
-> Emptiable (Stacked element) Possibly

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

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.l4 (0 |> Emptiable.filled) Emptiable.empty Emptiable.empty (3 |> Emptiable.filled)
    |> ArraySized.fills
    |> ArraySized.toStackEmptiable
--> Stack.topDown 0 [ 3 ]
--: Emptiable (Stacked Int) Possibly

Have >= 1 element? → Keep an Emptiable ... never_ toStackFilled

ArraySized (In (Fixed (Add1 minMinus1_)) max_) element
-> Emptiable (Stacked element) never_

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 Stack


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

Don't have >= 1 element? → toStackEmptiable

toString : ArraySized lengthRange_ Char -> 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 (Exactly N2) element -> T2 element element

Transform the ArraySized into a Toop.T2. This makes accessing elements and pattern matching easier.

to3 : ArraySized (Exactly N3) element -> T3 element element element

Transform the ArraySized into a Toop.T3. This makes accessing elements and pattern matching easier.

to4 :
ArraySized (Exactly N4) element
-> T4 element element element element

Transform the ArraySized into a Toop.T4. This makes accessing elements and pattern matching easier.

to5 :
ArraySized (Exactly N5) element
-> T5 element element element element element

Transform the ArraySized into a Toop.T5. This makes accessing elements and pattern matching easier.

to6 :
ArraySized (Exactly N6) element
-> T6 element element element element element element

Transform the ArraySized into a Toop.T6. This makes accessing elements and pattern matching easier.

to7 :
ArraySized (Exactly N7) element
-> T7 element element element element element element element

Transform the ArraySized into a Toop.T7. This makes accessing elements and pattern matching easier.

to8 :
ArraySized (Exactly N8) element
-> T8
element
element
element
element
element
element
element
element

Transform the ArraySized into a Toop.T8. This makes accessing elements and pattern matching easier.

to9 :
ArraySized (Exactly N9) element
-> T9
element
element
element
element
element
element
element
element
element

Transform the ArraySized into a Toop.T9. This makes accessing elements and pattern matching easier.

ArraySized (Exactly N10) element
-> T10
element
element
element
element
element
element
element
element
element
element

Transform the ArraySized into a Toop.T10. This makes accessing elements and pattern matching easier.

ArraySized (Exactly N11) element
-> T11
element
element
element
element
element
element
element
element
element
element
element

Transform the ArraySized into a Toop.T11. This makes accessing elements and pattern matching easier.

ArraySized (Exactly N12) element
-> T12
element
element
element
element
element
element
element
element
element
element
element
element

Transform the ArraySized into a Toop.T12. This makes accessing elements and pattern matching easier.

ArraySized (Exactly N13) element
-> T13
element
element
element
element
element
element
element
element
element
element
element
element
element

Transform the ArraySized into a Toop.T13. This makes accessing elements and pattern matching easier.

ArraySized (Exactly N14) element
-> T14
element
element
element
element
element
element
element
element
element
element
element
element
element
element

Transform the ArraySized into a Toop.T14. This makes accessing elements and pattern matching easier.

ArraySized (Exactly N15) element
-> T15
element
element
element
element
element
element
element
element
element
element
element
element
element
element
element

Transform the ArraySized into a Toop.T15. This makes accessing elements and pattern matching easier.

ArraySized (Exactly N16) element
-> T16
element
element
element
element
element
element
element
element
element
element
element
element
element
element
element
element

Transform the ArraySized into a Toop.T16. This makes accessing elements and pattern matching easier.

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

without internal functions

ArraySized (InFixed min max) element
-> ArraySized (InValue min max) element

ArraySized with a length of Fixed range → equatable Value range

ArraySized (InValue min max) element
-> ArraySized (InFixed min max) element

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

ArraySized (In (Fixed min) max) element
-> ArraySized (In (Value min) max) element

ArraySized with a length with a Fixed minimum → equatable Value minimum

ArraySized (In (Value min) max) element
-> ArraySized (In (Fixed min) max) element

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

ArraySized (In min (Fixed max)) element
-> ArraySized (In min (Value max)) element

ArraySized with a length with a Fixed maximum → equatable Value maximum

ArraySized (In min (Value max)) element
-> ArraySized (In min (Fixed max)) element

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

type-level

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

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

type alias Row =
    ArraySized (Exactly N18) Field

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

glue2TemporaryFields : Row -> ...
glue2TemporaryFields rowFromModelOrSomeStorage =
    ArraySized.repeat Temporary n2
        |> ArraySized.glue Up rowFromModelOrSomeStorage

Only Up<n> x can do that:

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

Have a specific minimum in mind? → minTo

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

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

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

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


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

More in N.minDown

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

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

type alias Row =
    ArraySized (Exactly N18) Field

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

glue2TemporaryFields : Row -> ...
glue2TemporaryFields rowFromModelOrSomeStorage =
    ArraySized.repeat Temporary n2
        |> ArraySized.glue Up rowFromModelOrSomeStorage

Only Up<n> x can do that:

glue2TemporaryFields :
    Row
    ->
        ArraySized
            (In (Up20 minX_) (Up20 maxX_))
            Field
glue2TemporaryFields rowFromModelOrSomeStorage =
    ArraySized.repeat Temporary n2
        |> ArraySized.glue 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)

maxUp 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 (In min max_) element -> ArraySized (Min min) element

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

between4And10Elements |> ArraySized.maxToInfinity
--: ArraySized (Min N4) ...

There is only 1 situation you should use this

To make these the same type

[ atLeast1Element, between1And10Elements ]

Elm complains:

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

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

Have a specific maximum in mind? → maxTo

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

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

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

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


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

More in N.maxUp