lue-bird/elm-typesafe-array/19.0.1

NOTE: the latest version is 34.0.1

ArraySized

An ArraySized describes an array where you know more about the amount of elements.

import Linear exposing (DirectionLinear(..))
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? Let's look at an example:

You have an array of 1+ elements. Get us the greatest value.

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


                Nothing ->
                    element
        )
        Nothing


withArr : ArraySized (In (Add1 minMinus1_) max_) comparable -> comparable
withArr =
    ArraySized.fold Up max

The Array type doesn't give us the info that it contains 1+ elements. ArraySized simply knows more about the length at compile time, so we can fold without a worry for example.

type alias ArraySized length element =
ArraySized length element

An Array where you know more about the amount of elements.

result type

-- amount >= 5
: ArraySized (Min N5) ...


-- 2 <= amount <= 12
: ArraySized (In N2 (Add12 a_)) ...

argument type

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


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


-- 4 <= amount <= 15
: ArraySized (In (Add4 minMinus4_) N15) ...

stored type

in your Model for example (which means: no type variables)

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


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


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

== crashes elm. Try other comparison methods

type alias In min max =
In min max {}

ArraySized length ranges are set up to not include differences as the specific N values n0, n1, ... do.

If you prefer to just use one type for both N and ArraySized

import N exposing (In)
In min max {}

nobody's stopping you :)

create

fromArray : Array element -> ArraySized (Min N0) element

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

arrayFromSomeOtherLibrary |> ArraySized.fromArray
--: ArraySized (Min N0)

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.until n6 -- big yes

"wrap early, unwrap late"

fromList : List element -> ArraySized (Min N0) element

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

listFromSomeOtherLibrary |> ArraySized.fromList
--: ArraySized (Min N0)

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.up n7 -- big yes

"wrap early, unwrap late"

Emptiable element possiblyOrNever_
-> ArraySized (In N0 (Add1 a_)) element

On Just ArraySized.l1, on Nothing empty.

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


filled "hi"
    |> ArraySized.fromEmptiable
    --: ArraySized (In N0 (Add1 a_))
    |> ArraySized.toList
--> [ "hi" ]


Emptiable.empty
    |> ArraySized.fromEmptiable
    --: ArraySized (In N0 (Add1 a_))
    |> ArraySized.toList
--> []

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

N (In min max difference_)
-> element
-> ArraySized (In min max) element

An ArraySized with a given amount of same elements.

import N exposing (n4)


ArraySized.repeat n4 'L'
    --: ArraySized (In N4 (Add4 a)) Char
    |> ArraySized.toList
--> [ 'L', 'L', 'L', 'L' ]


ArraySized.repeat atLeast3 'L'
--: ArraySized (Min N3) Char
N (In min max difference_)
-> Generator element
-> Generator (ArraySized (In min max) element)

Random.Generator for a given amount of elements.

import N exposing (n5)


ArraySized.random n5 (Random.float 0 1)


--: Random.Generator (ArraySized (In N5 (Add5 a_)) Float)
N.random
N (In min max difference_)
-> ArraySized (In (Add1 min) (Add1 max)) (N (In N0 max {}))

Increasing natural numbers until including a given number.

import N exposing (n3)


ArraySized.until n3
--: ArraySized
--:     (In N4 (Add4 a))
--:     (N (N.In N0 (Add3 a)))
    |> ArraySized.map N.toInt
    |> ArraySized.toList
--> [ 0, 1, 2, 3 ]


ArraySized.until between2And9
    |> ArraySized.map (N.add n3)
--: ArraySized
--:    (In N3 (Add10 a))
--:    (N (N.In N3 (Add12 a)))

specific

empty : ArraySized (In N0 atLeast0_) element_

No elements

ArraySized.empty
--: ArraySized (In N0 atLeast0_) element
    |> ArraySized.push ":)"
--: ArraySized (In N1 (Add1 atLeast0_)) String
l1 : element -> ArraySized (In N1 (Add1 a_)) element

Create an ArraySized with exactly 1 element

l2 : element -> element -> ArraySized (In N2 (Add2 a_)) element

Create an ArraySized with exactly 2 elements in this order

l3 : element -> element -> element -> ArraySized (In N3 (Add3 a_)) element

Create an ArraySized with exactly 3 elements in this order

l4 :
element
-> element
-> element
-> element
-> ArraySized (In N4 (Add4 a_)) element

Create an ArraySized with exactly 4 elements in this order

l5 :
element
-> element
-> element
-> element
-> element
-> ArraySized (In N5 (Add5 a_)) element

Create an ArraySized with exactly 5 elements in this order

l6 :
element
-> element
-> element
-> element
-> element
-> element
-> ArraySized (In N6 (Add6 a_)) element

Create an ArraySized with exactly 6 elements in this order

l7 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized (In N7 (Add7 a_)) element

Create an ArraySized with exactly 7 elements in this order

l8 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized (In N8 (Add8 a_)) element

Create an ArraySized with exactly 8 elements in this order

l9 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized (In N9 (Add9 a_)) element

Create an ArraySized with exactly 9 elements in this order

l10 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized (In N10 (Add10 a_)) element

Create an ArraySized with exactly 10 elements in this order

l11 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized (In N11 (Add11 a_)) element

Create an ArraySized with exactly 11 elements in this order

l12 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized (In N12 (Add12 a_)) element

Create an ArraySized with exactly 12 elements in this order

l13 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized (In N13 (Add13 a_)) element

Create an ArraySized with exactly 13 elements in this order

l14 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized (In N14 (Add14 a_)) element

Create an ArraySized with exactly 14 elements in this order

l15 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized (In N15 (Add15 a_)) element

Create an ArraySized with exactly 15 elements in this order

l16 :
element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> ArraySized (In N16 (Add16 a_)) element

Create an ArraySized with exactly 16 elements in this order

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

scan

length : ArraySized length element_ -> N length

The amount of elements.

import N exposing (n3)


ArraySized.l3 1 2 3
    |> ArraySized.length
    --: N (N.In N3 (Add3 a_))
    |> N.toInt
--> 3


between3And5Elements |> ArraySized.length
--: N (N.In N3 (Add5 a_))


atLeast3Elements |> ArraySized.length
--: N (Min N3)
( DirectionLinear
, N (In indexMin_ minLengthMinus1 indexDifference_)
)
-> ArraySized (In (Add1 minLengthMinus1) maxLength_) element
-> element

The element at a valid position in a direction.

import Linear exposing (DirectionLinear(..))
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
to1 : ArraySized (Exactly N1) element -> element

Access its only value

ArraySized.l1 "hi" |> ArraySized.to1
--> "hi"
areAll : (element -> Bool) -> ArraySized length_ element -> Bool

Whether all elements satisfy a test.

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


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


ArraySized.empty |> ArraySized.areAll (\n -> n <= 4)
--> True
isAny : (element -> Bool) -> ArraySized length_ element -> Bool

Whether any elements satisfy a test.

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


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


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

alter

( DirectionLinear, N index_ )
-> (() -> element)
-> ArraySized length element
-> ArraySized length element

Set the element at an index in a direction.

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


ArraySized.l3 "I" "am" "ok"
    |> ArraySized.elementReplace ( Up, n2 )
        (\() -> "confusion")
    --: ArraySized (In n3 (Add3 a)) String
    |> ArraySized.toList
--> [ "I", "am", "confusion" ]


ArraySized.l3 "I" "am" "ok"
    |> ArraySized.elementReplace ( Down, n1 )
        (\() -> "feel")
    --: ArraySized (In n3 (Add3 a)) String
    |> ArraySized.toList
--> [ "I", "feel", "ok" ]

An index that's out of bounds is ignored and the element replacement is never evaluated.

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


ArraySized.l3 "I" "am" "ok"
    |> ArraySized.elementReplace ( Down, n3 )
        (\() -> "feel")
    --: ArraySized (In n3 (Add3 a)) String
    |> ArraySized.toList
--> [ "I", "am", "ok" ]
( DirectionLinear, N index_ )
-> (element -> element)
-> ArraySized length element
-> ArraySized length element

Change the element at an index in a direction based on its previous value.

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


ArraySized.l3 1 20 30
    |> ArraySized.elementAlter ( Up, n0 ) (\x -> x * 10)
    --: ArraySized (In n3 (Add3 a)) number_
    |> ArraySized.toList
--> [ 10, 20, 30 ]


ArraySized.l3 1 20 30
    |> ArraySized.elementAlter ( Down, n0 ) (\x -> -x)
    --: ArraySized (In n3 (Add3 a)) number_
    |> ArraySized.toList
--> [ 1, 20, -30 ]

An index that's out of bounds is ignored.

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


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

Alias to ArraySized.order Down: flip the order of the elements.

ArraySized.l4 "l" "i" "v" "e"
    |> ArraySized.reverse
    --: ArraySized (In N4 (Add4 a_)) String
    |> ArraySized.toList
--> [ "e", "v", "i", "l" ]
element
-> ArraySized (In min max) element
-> ArraySized (In (Add1 min) (Add1 max)) element

Equivalent to insert n0 Down. Put a new element after all the others.

between5And10Elements
    |> ArraySized.push "becomes the last"
--: ArraySized (In N6 (Add11 a_)) String
element
-> ArraySized (In min maxLength_) element
-> ArraySized (Min (Add1 min)) element

Put a new element after the others.

atLeast5Elements
    |> ArraySized.minPush "becomes the last"
--: ArraySized (Min N6) String
( DirectionLinear
, N (In indexMin_ minLengthMinus1 indexDifference_)
)
-> ArraySized
(In (Add1 minLengthMinus1) (Add1 maxLengthMinus1))
element
-> ArraySized (In minLengthMinus1 maxLengthMinus1) element

Kick an element out of the ArraySized at a given index in a direction

import Linear exposing (DirectionLinear(..))
import N exposing (n5)


removeLast between1And10Elements =
    between1And10Elements
        |> ArraySized.elementRemove ( Down, n0 )
( DirectionLinear
, N (In indexMin_ minLengthMinus1 indexDifference_)
)
-> ArraySized (In (Add1 minLengthMinus1) max) element
-> ArraySized (In minLengthMinus1 max) element

Kick out the element at an index in a direction.

removeLast =
    TypeSized.minElementRemove ( Down, n0 )
( DirectionLinear, N (In indexMin_ min indexDifference_) )
-> element
-> ArraySized (In min max) element
-> ArraySized (In (Add1 min) (Add1 max)) element

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

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


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


ArraySized.l3 'a' 'c' 'd'
    |> ArraySized.insert ( Down, n2 ) 'b'
    --: ArraySized (In N4 (Add4 a_)) Char
    |> ArraySized.toList
--> [ 'a', 'b', 'c', 'd' ]
( DirectionLinear, N (In indexMin_ min indexDifference_) )
-> element
-> ArraySized (In min maxLength_) element
-> ArraySized (Min (Add1 min)) element

Put a new element at an index in a direction.

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


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


cons :
    element
    -> ArraySized (In min maxLength_) element
    -> ArraySized (Min (Add1 min)) element
cons =
    ArraySized.minInsert ( Up, n0 )
( N
(In
min
atLeastMinLength_
(Is (Diff min To (Add1 minDoubleLengthMinus1)) minDiff1_)
)
, N
(In
max
atLeastMaxLength_
(Is maxDiff0_ (Diff max To (Add1 maxDoubleLengthMinus1)))
)
)
-> element
-> ArraySized (In min max) element
-> ArraySized
(In minDoubleLengthMinus1 maxDoubleLengthMinus1)
element

Place a value between all members.

To get the correct final length type, we need to give the current ( minimum , maximum ) length as an arguments.

import N exposing (n3)


ArraySized.l3 "turtles" "turtles" "turtles"
    |> ArraySized.intersperseIn ( n3, n3 ) "on"
    --: ArraySized (In N5 (Add5 a_)) String
    |> ArraySized.toList
--> [ "turtles", "on", "turtles", "on", "turtles" ]
N
(In
min
minAtLeast_
(Is (Diff min To (Add1 minDoubleMinus1)) minDiff1_)
)
-> element
-> ArraySized (In min maxLength_) element
-> ArraySized (Min minDoubleMinus1) element

Place a value between all members.

To get the correct final length type, we need to give the current minimum length as an arguments.

import N exposing (n3)


atLeast3Turtles
    |> ArraySized.intersperseAtLeast n3 "on"
--→ "turtles" "on" "turtles" "on" "turtles" ...
--: ArraySized (Min N5) String

filter

ArraySized (In min_ max) (Emptiable value possiblyOrNever_)
-> ArraySized (In N0 max) value

Take every filled value, drop every empty.

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


ArraySized.l3 (filled "This") Emptiable.empty (filled "fine")
    |> ArraySized.fills
    --: ArraySized (In N0 (Add3 a_)) String
    |> ArraySized.toList
--> [ "This", "fine" ]

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

import N exposing (n0)


ArraySized.l3 "1.2" "2" "hello"
    |> ArraySized.map (String.toInt >> Emptiable.fromMaybe)
    |> ArraySized.fills
    --: ArraySized (In N0 (Add3 a_)) Int
    |> ArraySized.toList
--> [ 2 ]
ArraySized length (Emptiable value possiblyOrNever)
-> Emptiable (ArraySized length 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.areAllFilled
    |> fillMap ArraySized.toList
--> filled []


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


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

Funnily, this can sometimes even be nicer than mapN/andMap:

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


-- vs
groupCall =
    map5 Toop.T5
        aUser.phoneNumber
        bUser.phoneNumber
        cUser.phoneNumber
        dUser.phoneNumber
        eUser.phoneNumber

part

DirectionLinear
-> N
(In
dropped_
droppedAtLeast_
(Is (Diff minTaken To min) (Diff maxTaken To max))
)
-> ArraySized (In min max) element
-> ArraySized (In minTaken maxTaken) element

Elements after a certain number of elements in a direction.

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


ArraySized.l4 0 1 2 3
    |> ArraySized.drop Down n2
    --: ArraySized (In N2 (Add2 a_)) number_
    |> ArraySized.toList
--> [ 0, 1 ]


between6And10Elements
    |> ArraySized.drop Up n2
--: ArraySized (In N4 (Add10 a_)) ...
DirectionLinear
-> N (In dropped_ atLeastDropped_ (Is (Diff minTaken To min) is_))
-> ArraySized (In min max) element
-> ArraySized (In minTaken max) element

Elements after a certain number of elements in a direction.

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


atLeast6Elements
    |> ArraySized.minDrop Down n2
--: ArraySized (Min N4) ...
DirectionLinear
-> N (In taken atLeastTaken (Is (Diff minNotTaken_ To min) is_))
-> ArraySized (In min maxLength_) element
-> ArraySized (In taken atLeastTaken) element

A certain number of elements from a direction.

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


ArraySized.l8 0 1 2 3 4 5 6 7
    |> ArraySized.take Up n7
    --: ArraySized (In N7 (Add7 a_)) number_
    |> ArraySized.toList
--> [ 0, 1, 2, 3, 4, 5, 6 ]

Use takeAtMost if you don't know the exact amount of elements to take.

DirectionLinear
-> N
(In
maxTaken
atLeastMaxTaken
(Is (Diff minNotTaken_ To min) is_)
)
-> N (In minTaken maxTaken takenDifference_)
-> ArraySized (In min maxLength_) element
-> ArraySized (In minTaken atLeastMaxTaken) element

A certain number of elements from a direction.

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


ArraySized.l8 0 1 2 3 4 5 6 7
    |> ArraySized.takeAtMost Up n7 between3And7
--: ArraySized (In N3 (Add7 a_)) ...

The first number is the maximum taken amount. The second number is the amount of taken elements.

Use take if you know the exact amount of elements to take.

N
(In
(Add1 chunkSizeMinMinus1)
(Add1 chunkSizeMaxMinus1)
chunkSizeDifference_
)
-> { remainder : DirectionLinear }
-> ArraySized (In minLength_ max) element
-> { chunks :
ArraySized
(In N0 max)
(ArraySized
(In (Add1 chunkSizeMinMinus1) (Add1 chunkSizeMaxMinus1))
element
)
, remainder : ArraySized (In N0 chunkSizeMaxMinus1) element
}

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

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

↓

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


ArraySized.l7 1 2 3 4 5 6 7
    |> ArraySized.toChunksOf n5 { remainder = Up }
    --: { chunks :
    --:     ArraySized (In N0 (Add7 a_))
    --:         (ArraySized (In N5 (Add5 b_)) number_)
    --: remainder :
    --:     ArraySized (In N0 (Add5 c_)) number_
    --: }
    |> .remainder
    |> ArraySized.toList
--> [ 6, 7 ]




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

glueing

DirectionLinear
-> N
(In
added
atLeastAdded_
(Is (Diff min To minLengthSum) (Diff max To minSumMax))
)
-> ArraySized (Exactly added) element
-> ArraySized (In min max) element
-> ArraySized (In minLengthSum minSumMax) element

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

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


ArraySized.l3 1 2 3
    |> ArraySized.glue Up n3 (ArraySized.l3 4 5 6)
    --: ArraySized (In n6 (Add6 a_)) number_
    |> ArraySized.toList
--> [ 1, 2, 3, 4, 5, 6 ]


ArraySized.l3 1 2 3
    |> ArraySized.glue Down n3 (ArraySized.l3 4 5 6)
    --: ArraySized (In n6 (Add6 a_)) number_
    |> ArraySized.toList
--> [ 4, 5, 6, 1, 2, 3 ]
DirectionLinear
-> ( N
(In
addedMin
atLeastAddedMin_
(Is (Diff min To minLengthSum) addedMinDiff1_)
)
, N
(In
addedMax
atLeastAddedMax_
(Is addedMaxDiff0_ (Diff max To maxLengthSum))
)
)
-> ArraySized (In addedMin addedMax) element
-> ArraySized (In min max) element
-> ArraySized (In minLengthSum maxLengthSum) element

Attach elements of an ArraySized which has multiple possible amounts to a given direction.

import Linear exposing (DirectionLinear(..))
import N exposing (n3, n5)


ArraySized.l3 1 2 3
    |> ArraySized.glueIn Up
        ( n3, n5 )
        between3And5Elements
--: ArraySized (In N6 (Add8 a))


ArraySized.l3 1 2 3
    |> ArraySized.glueIn Down
        ( n3, n5 )
        between3And5Elements
--: ArraySized (In N6 (Add8 a))

Use glue to glue an Exact amount of elements.

DirectionLinear
-> N
(In
minAdded
atLeastMinAdded_
(Is (Diff min To minLengthSum) is_)
)
-> ArraySized (In minAdded maxAdded_) element
-> ArraySized (In min maxLength_) element
-> ArraySized (Min minLengthSum) element

Attach elements of an ArraySized to a given direction.

ArraySized.l3 1 2 3
    |> ArraySized.glueAtLeast Up n3 atLeast3Elements
--: ArraySized (Min N6) ...


ArraySized.l3 1 2 3
    |> ArraySized.glueAtLeast Down n3 atLeast3Elements
--: ArraySized (Min N6) ...

length compare

( N
(In
lowerLimitMin
(Add1 lowerLimitMaxMinus1)
lowerLimitDifference_
)
, N (In upperLimitMin upperLimitMax upperLimitDifference_)
)
-> ArraySized (In min max) element
-> Result
(BelowOrAbove
(ArraySized (In min lowerLimitMaxMinus1) element)
(ArraySized (In (Add1 upperLimitMin) 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
comparedAgainstMin
(Add1 comparedAgainstMaxMinus1)
comparedAgainstDifference_
)
-> ArraySized (In min max) element
-> Result
(BelowOrAbove
(ArraySized (In min comparedAgainstMaxMinus1) element)
(ArraySized (In (Add1 comparedAgainstMin) max) element)
)
(ArraySized
(In comparedAgainstMin (Add1 comparedAgainstMaxMinus1))
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
(Add1 lowerLimitMaxMinus1)
lowerLimitDifference_
)
-> ArraySized (In min max) element
-> Result
(ArraySized (In min lowerLimitMaxMinus1) element)
(ArraySized (In lowerLimitMin max) element)

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

import N exposing (n5)


first5 :
    ArraySized (In minLength_ max) element
    -> Maybe (ArraySized (In N5 max) element)
first5 arraySized =
    case arraySized |> ArraySized.hasAtLeast n5 of
        N.Below _ ->
            Nothing


        N.EqualOrGreater atLeast5 ->
            Just atLeast5
N (In upperLimitMin upperLimitMax upperLimitDifference_)
-> ArraySized (In min max) element
-> Result
(ArraySized (In (Add1 upperLimitMin) 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 orHigherMin_) N50) String
    -> a
    -> Tagged a


tagIfValidTags :
    ArraySized (In (Add3 orHigherMin_) max)
    -> a
    -> Maybe (Tagged a)
tagIfValidTags array value =
    case
        array
            |> ArraySized.fromArray
            |> ArraySized.hasAtMost n50
    of
        Ok atMost53 ->
            tag value atMost53 |> Just


        Err _ ->
            Nothing

transform

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

Change every element.

import N exposing (n25)


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


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

Oh look, more type-safety!

result
-> DirectionLinear
-> (element -> result -> result)
-> ArraySized length_ element
-> result

Reduce an ArraySized in a direction.

import Linear exposing (DirectionLinear(..))


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)
DirectionLinear
-> (element -> element -> element)
-> ArraySized (In (Add1 minLengthMinus1_) maxLength_) element
-> element

A fold in a direction where the initial result is the first element in the ArraySized.

import Linear exposing (DirectionLinear(..))


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


ArraySized.l3 "go" "to" "uni"
    |> ArraySized.fold Down
        (\word soFar -> soFar ++ " " ++ word)
--> "uni to go"
toArray : ArraySized length_ 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.until n4
    |> ArraySized.map N.toInt
    |> ArraySized.toArray
--> Array.fromList [ 0, 1, 2, 3, 4 ]
toList : ArraySized length_ 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.until n4
    |> ArraySized.map N.toInt
    |> ArraySized.toList
--> [ 0, 1, 2, 3, 4 ]
toEmptiable : ArraySized (In min_ 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.

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

type information

N (In newMinLength min newMinDifference_)
-> ArraySized (In min max) element
-> ArraySized (In newMinLength max) element

Use a lower minimum length in the type.

[ atLeast3Elements
, atLeast4Elements
]

elm complains that

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

[ atLeast3Elements
, atLeast4Elements
    |> ArraySized.minDownLength n3
]
N (In max newMax difference_)
-> ArraySized (In min max) element
-> ArraySized (In min newMax) element

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

Designing argument and stored types as broad as possible:

atMost18Elements : ArraySized (In min_ N18) ...

The argument in atMost18Elements should also fit in atMost19Elements for example

atMost19Elements theArgument -- error


atMost19Elements (theArgument |> ArraySized.maxOpen n18)
noMax : ArraySized (In min max_) element -> ArraySized (Min min) element

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

between4And10Elements |> ArraySized.toMin
--: 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.toMin
]
N
(In
increase_
increaseAtLeast_
(Is (Diff max To maxIncreased) diff1_)
)
-> ArraySized (In min max) element
-> ArraySized (In min maxIncreased) element

Have a specific maximum in mind? → maxOpen

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

maxUp4 : ArraySized (In min max) -> ArraySized (In min (Add4 max))
maxUp4 =
    ArraySized.maxUp n4

When is this useful? Very rarely, to preserve type variables. More in N.maxUp