lue-bird/elm-typesafe-array/18.2.0

NOTE: the latest version is 34.0.1

Arr

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

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


Arr.empty |> Arr.at nat0 FirstToLast
--> 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 =
    array
        |> Array.foldl
            (\a mayB ->
                case maybeC of
                    Just b ->
                        max a b


                    Nothing ->
                        a
            )
            Nothing


withArr : Arr (In (Nat1Plus minMinus1_) max_) comparable -> comparable
withArr =
    Arr.foldWith FirstToLast max

The Array type doesn't give us the info that it contains 1+ elements. Arr simply knows more about the length at compile time, so you can e.g. call foldWith without a worry.

type alias Arr length element =
Typed Checked ArrTag Internal (Content length element)

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

types

  • amount >= 5
Arr (Min Nat5) ...
  • 2 <= amount <= 12
Arr (In Nat2 (Nat12Plus a_)) ...

as argument types

  • = 15
Arr (Only Nat15) ...
  • amount >= 4
Arr (In (Nat4Plus minMinus4_) max_) ...
  • 4 <= amount <= 15
Arr (In (Nat4Plus minMinus4_) Nat15) ...

as storage types

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

  • = 15
Arr (Only Nat15) ...
  • amount >= 4
Arr (Min Nat4) ...
  • 4 <= amount <= 15
Arr (In Nat4 Nat15) ...
type alias ArrTag =
ArrTag

Internally tags a valid Arr.

create

fromArray : Array element -> Arr (Min Nat0) element

Create an Arr from an Array.

As every Array has >= 0 elements:

Arr.fromArray arrayFromSomewhere
--> : Arr (Min Nat0)

Don't use it this way:

Arr.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!

Arr.from7 0 1 2 3 4 5 6
--> ok


Arr.nats nat7
--> big yes
fromNonEmptyList : ( element, List element ) -> Arr (Min Nat1) element

Convert a non-empty list tuple into an Arr (Min Nat1).

Don't use it this way:

Arr.fromNonEmptyList ( 0, [ 1, 2, 3, 4, 5, 6 ] )
--> big no!

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

Arr.from7 0 1 2 3 4 5 6
--> ok


Arr.nats nat7
--> big yes
fromList : List element -> Arr (Min Nat0) element

Short for Array.fromList list |> Arr.fromArray. Convert a List to an Arr (Min Nat0).

Don't use it this way:

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

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

Arr.from7 0 1 2 3 4 5 6
--> ok


Arr.nats nat7
--> big yes
fromMaybe : Maybe element -> Arr (In Nat0 (Nat1Plus a_)) element

Arr.from1 if Just, empty if Nothing.

Arr.fromMaybe (Just "hi")
--> Arr.from1 "hi"
--> : Arr (In Nat0 (Nat1Plus a_))


Arr.fromMaybe Nothing
--> Arr.empty
--> : Arr (In Nat0 (Nat1Plus a_))
repeat : Nat (ArgIn min max ifN_) -> element -> Arr (In min max) element

An Arr with a given amount of same elements.

Arr.repeat nat4 'L'
--> Arr.from4 'L' 'L' 'L' 'L'


Arr.repeat atLeast3 'L'
--> : Arr (Min Nat3) Char
Nat (ArgIn min (Nat1Plus maxMinus1) ifN_)
-> Arr (In min (Nat1Plus maxMinus1)) (Nat (In Nat0 maxMinus1))

Increasing natural numbers until below a given value.

Arr.nats nat4
--> Arr.from4 (Nat 0) (Nat 1) (Nat 2) (Nat 3)
--> : Arr
-->     (In Nat4 (Nat4PLus a))
-->     (Nat (In Nat0 (Nat3Plus a)))


Arr.nats between2And10
--> : Arr
-->     (In Nat2 (Nat10Plus a))
-->     (Nat (In Nat0 (Nat9Plus a)))


from first length =
    Arr.nats length
        |> Arr.map (InNat.add first)

If you want to use a Nat as the length, but you don't know the maximum, e.g.

Arr.nats (Nat.intAtLeast nat5 someInt) -- error

use minNats.

minNats : Nat (ArgIn min max ifN_) -> Arr (In min max) (Nat (In Nat0 max))

Increasing natural numbers until below a given value.

Use nats if you know the maximum length:

Arr.nats nat5


Arr.nats between2And10

If you don't:

Arr.minNats atLeast10
--> : Arr (Min Nat10)
-->     (Nat (Min Nat10))


from first length =
    Arr.minNats length
        |> Arr.map (MinNat.add first)
Nat (ArgIn minLength maxLength ifN_)
-> Generator element
-> Generator (Arr (In minLength maxLength) element)

Generate a given amount of elements and put them in an Arr.

Arr.random nat5 (Random.float 0 1)


--> : Random.Generator (Arr (In Nat5 (Nat5Plus a_)) Float)
Nat.random

exact

empty : Arr (In Nat0 atLeast0_) element_

No elements.

Arr.empty
--> : Arr (In Nat0 atLeast0_) element
    |> InArr.push ":)"
--> : Arr (In Nat1 (Nat1Plus atLeast0_)) String
from1 : element -> Arr (In Nat1 (Nat1Plus a_)) element

Create an Arr with exactly 1 element.

from2 : element -> element -> Arr (In Nat2 (Nat2Plus a_)) element

Create an Arr with exactly 2 elements in this order.

from3 : element -> element -> element -> Arr (In Nat3 (Nat3Plus a_)) element

Create an Arr with exactly 3 elements in this order.

element
-> element
-> element
-> element
-> Arr (In Nat4 (Nat4Plus a_)) element

Create an Arr with exactly 4 elements in this order.

element
-> element
-> element
-> element
-> element
-> Arr (In Nat5 (Nat5Plus a_)) element

Create an Arr with exactly 5 elements in this order.

element
-> element
-> element
-> element
-> element
-> element
-> Arr (In Nat6 (Nat6Plus a_)) element

Create an Arr with exactly 6 elements in this order.

element
-> element
-> element
-> element
-> element
-> element
-> element
-> Arr (In Nat7 (Nat7Plus a_)) element

Create an Arr with exactly 7 elements in this order.

element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> Arr (In Nat8 (Nat8Plus a_)) element

Create an Arr with exactly 8 elements in this order.

element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> Arr (In Nat9 (Nat9Plus a_)) element

Create an Arr with exactly 9 elements in this order.

element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> Arr (In Nat10 (Nat10Plus a_)) element

Create an Arr with exactly 10 elements in this order.

element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> Arr (In Nat11 (Nat11Plus a_)) element

Create an Arr with exactly 11 elements in this order.

element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> Arr (In Nat12 (Nat12Plus a_)) element

Create an Arr with exactly 12 elements in this order.

element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> Arr (In Nat13 (Nat13Plus a_)) element

Create an Arr with exactly 13 elements in this order.

element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> Arr (In Nat14 (Nat14Plus a_)) element

Create an Arr with exactly 14 elements in this order.

element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> Arr (In Nat15 (Nat15Plus a_)) element

Create an Arr with exactly 15 elements in this order.

element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> element
-> Arr (In Nat16 (Nat16Plus a_)) element

Create an Arr with exactly 16 elements in this order.

scan

length : Arr length element_ -> Nat length

The amount of elements.

Arr.from3 1 2 3 |> Arr.length
--> nat3 : Nat (In Nat3 (Nat3Plus a_))


between3And5Elements |> Arr.length
--> : Nat (In Nat3 (Nat5Plus a_))


atLeast3Elements |> Arr.length
--> : Nat (Min Nat3)
at :
Nat (ArgIn indexMin_ minLengthMinus1 indexIfN_)
-> LinearDirection
-> Arr (In (Nat1Plus minLengthMinus1) maxLength_) element
-> element

The element at a valid position in a direction.

Arr.from4 0 1 2 3
    |> Arr.at nat1 FirstToLast
--> 1


Arr.from4 0 1 2 3
    |> Arr.at nat1 LastToFirst
--> 2
all : (element -> Bool) -> Arr length_ element -> Bool

Whether all elements satisfy a test.

Arr.all isEven (Arr.from2 2 4)
--> True


Arr.all isEven (Arr.from2 2 3)
--> False


Arr.all isEven Arr.empty
--> True
any : (element -> Bool) -> Arr length_ element -> Bool

Whether any elements satisfy a test.

Arr.any isEven (Arr.from2 1 3)
--> True


Arr.any isEven (Arr.from2 1 2)
--> False


Arr.any isEven Arr.empty
--> False

modify

Nat index_
-> LinearDirection
-> element
-> Arr length element
-> Arr length element

Set the element at an index in a direction.

Arr.from3 "I" "am" "ok"
    |> Arr.replaceAt nat2 FirstToLast "confusion"
--> Arr.from3 "I" "am" "confusion"


Arr.from3 "I" "am" "ok"
    |> Arr.replaceAt nat1 LastToFirst "feel"
--> Arr.from3 "I" "feel" "ok"

An index that's out of bounds is ignored.

Arr.from3 "I" "am" "ok"
    |> Arr.replaceAt nat3 LastToFirst "feel"
--> Arr.from3 "I" "am" "ok"
Nat index_
-> LinearDirection
-> (element -> element)
-> Arr length element
-> Arr length element

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

Arr.from3 1 20 30
    |> Arr.updateAt nat0 FirstToLast ((*) 10)
--> Arr.from3 10 20 30


Arr.from3 1 20 30
    |> Arr.updateAt nat0 LastToFirst (\x -> -x)
--> Arr.from3 1 20 -30

An index that's out of bounds is ignored.

Arr.from3 1 20 30
    |> Arr.updateAt nat3 FirstToLast ((*) 10)
--> Arr.from3 1 20 30
LinearDirection
-> Nat (ArgIn newMinLength newMaxLength ifN_)
-> element
-> Arr length_ element
-> Arr (In newMinLength newMaxLength) element

Resize an Arr in a direction, padding with a given value.

Arr.from2 1 2
    |> Arr.resize LastToFirst nat4 0
--> Arr.from4 0 0 1 2


Array.from3 1 2 3
    |> Arr.resize LastToFirst nat2 0
--> Arr.from2 2 3


Array.from2 1 2
    |> Arr.resize FirstToLast nat4 0
--> Arr.from4 1 2 0 0


Array.from3 1 2 3
    |> Arr.resize FirstToLast nat2 0
--> Arr.from2 1 2

This is a quick way to gain some type-level knowledge about the length.

reverse : Arr length element -> Arr length element

Alias to Arr.order LastToFirst: flip the order of the elements.

Arr.from4 "l" "i" "v" "e"
    |> Arr.reverse
--> Arr.from4 "e" "v" "i" "l"
order : LinearDirection -> Arr length element -> Arr length element

Keep the order if FirstToLast, reverse if LastToFirst.

Arr.from3 1 2 3
    |> Arr.order LastToFirst
--> Arr.from3 3 2 1


Arr.from3 1 2 3
    |> Arr.order FirstToLast
--> Arr.from3 1 2 3

filter

(element -> Bool)
-> Arr (In minLength_ maxLength) element
-> Arr (In Nat0 maxLength) element

Only keep values that satisfy a test.

Arr.from5 1 2 3 4 5
    |> Arr.when (\n -> n >= 3)
--> Arr.from3 3 4 5
--> : Arr (In Nat0 (Nat5Plus a_)) number_
(element -> Bool)
-> Arr (In minLength_ maxLength) element
-> Arr (In Nat0 maxLength) element

Remove values where a condition is met.

Arr.from5 1 2 3 4 5
    |> Arr.dropWhen (\n -> n < 3)
--> Arr.from3 3 4 5
--> : Arr (In Nat0 (Nat5Plus a_)) number_
Arr (In minLength_ maxLength) (Maybe value)
-> Arr (In Nat0 maxLength) value

Take every Just value & drop every Nothing.

Arr.from3 (Just "This") Nothing (Just "fine")
    |> Arr.whenJust
--> Arr.from2 "This" "fine"
--> : Arr (In Nat0 (Nat3Plus a_)) String

Call map and then whenJust to get the same functionality as "filterMap".

Arr.from3 "1.2" "2" "hello"
    |> Arr.map String.toInt
    |> Arr.whenJust
--> Arr.from1 2
--> : Arr (In Nat0 (Nat3Plus a_)) Int
whenAllJust : Arr length (Maybe value) -> Maybe (Arr length value)

If every Maybe is present, return all of the values. Return Nothing if any element is Nothing.

Arr.empty |> Arr.whenAllJust
--> Just Arr.empty


Arr.from3 (Just 1) (Just 2) (Just 3)
    |> Arr.whenAllJust
--> Just (Arr.from3 1 2 3)


Arr.from3 (Just 1) Nothing (Just 3)
    |> Arr.whenAllJust
--> Nothing

Funnily, this can sometimes even be nicer than Maybe.mapN:

groupCall =
    Arr.from5 aUser bUser cUser dUser eUser
        |> Arr.map .phoneNumber
        |> Arr.whenAllJust


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

part

Nat (N taken atLeastTaken (Is minNotTaken_ To minLength) is_)
-> LinearDirection
-> Arr (In minLength maxLength_) element
-> Arr (In taken atLeastTaken) element

A certain number of elements from a direction.

Arr.from8 0 1 2 3 4 5 6 7
    |> Arr.take nat7 FirstToLast
--> Arr.from7 0 1 2 3 4 5 6

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

Nat
(N maxTaken atLeastMaxTaken (Is minNotTaken_ To minLength) is_)
-> Nat (ArgIn minTaken maxTaken takenIfN_)
-> LinearDirection
-> Arr (In minLength maxLength_) element
-> Arr (In minTaken atLeastMaxTaken) element

A certain number of elements from a direction.

Arr.from8 0 1 2 3 4 5 6 7
    |> Arr.takeMax nat7 between3And7 FirstToLast
--> : Arr (In Nat3 (Nat7Plus 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.

Nat
(ArgIn
(Nat1Plus minGroupSizeMinus1)
maxGroupSize
groupSizeIfN_
)
-> LinearDirection
-> Arr (In minLength_ maxLength) element
-> { groups :
Arr
(In Nat0 maxLength)
(Arr (In (Nat1Plus minGroupSizeMinus1) maxGroupSize) element)
, remaining : Arr (In Nat0 maxGroupSize) element
}

Split the Arr into equal-sized chunks in a direction.

  • groups: the Arr divided into equal-sized Arrs
  • less: values to one side that don't fill a whole group
Arr.from7 1 2 3 4 5 6 7
    |> Arr.groupsOf nat5 FirstToLast
--> { groups =
-->     Arr.from1 (Arr.from5 1 2 3 4 5)
-->     : Arr (In Nat0 (Nat7Plus a_))
-->         (Arr (In Nat5 (Nat5Plus b_)) number_)
--> , remaining =
-->     Arr.from2 6 7
-->     : Arr (In Nat0 (Nat5Plus c_)) number_
--> }

Arr.from7 1 2 3 4 5 6 7
    |> Arr.groupsOf nat5 LastToFirst
--> { groups = Arr.from1 (Arr.from5 3 4 5 6 7) : ...
--> , remaining = Arr.from2 1 2 : ...
--> }

transform

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

Change every element.

aToZ : Arr (In Nat26 (Nat26Plus a_)) Char
aToZ =
    Arr.nats nat26
        |> Arr.map (val >> inABC)


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

val refers to Typed.val.

LinearDirection
-> (element -> element -> element)
-> Arr (In (Nat1Plus minLengthMinus1_) maxLength_) element
-> element

A fold in a direction where the initial result is the first value in the Arr.

Arr.from3 234 345 543
    |> Arr.foldWith FirstToLast max
--> 543


Arr.foldWith LastToFirst
    (\word soFar -> soFar ++ " " ++ word)
    (Arr.from3 "go" "to" "school")
--> "school to go"
LinearDirection
-> (element -> result -> result)
-> result
-> Arr length_ element
-> result

Reduce an Arr in a direction.

Arr.from5 'l' 'i' 'v' 'e'
    |> Arr.fold LastToFirst String.cons ""
--> "live"


Arr.from5 'l' 'i' 'v' 'e'
    |> Arr.fold FirstToLast String.cons ""
--> "evil"


sum =
    Arr.fold FirstToLast (+) 0


product =
    Arr.fold FirstToLast (*) 0
toArray : Arr length_ element -> Array element

Convert the Arr to an Array. Just do this in the end. Try to keep extra information as long as you can.

Arr.nats nat5
    |> Arr.map val
    |> Arr.toArray
--> Array.fromList [ 0, 1, 2, 3, 4 ]

val refers to Typed.val.

Arr (In (Nat1Plus minLengthMinus1_) maxLength_) element
-> ( element, List element )

Convert the Arr to a List. Make these kinds of operations the final step. Try to keep extra information as long as you can.

Arr.nats nat5
    |> Arr.map val
    |> Arr.toNonEmptyList
--> ( 0, [ 1, 2, 3, 4 ] )

val refers to Typed.val.

toList : Arr length_ element -> List element

Short for Arr.toArray arr |> Array.toList. Convert the Arr to a List. Make these kinds of operations the final step. Try to keep extra information as long as you can.

Arr.nats nat5
    |> Arr.map val
    |> Arr.toList
--> [ 0, 1, 2, 3, 4 ]

val refers to Typed.val.

toMaybe : Arr (In minLength_ Nat1) element -> Maybe element

Nothing if the Arr is empty, else Just it's only value.

extract

to1 : Arr (Only Nat1) element -> element

Access the only value in the Arr.

Arr.to1 (Arr.from1 "hi")
--> "hi"
to2 : Arr (Only Nat2) element -> T2 element element

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

to3 : Arr (Only Nat3) element -> T3 element element element

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

to4 : Arr (Only Nat4) element -> T4 element element element element

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

to5 :
Arr (Only Nat5) element
-> T5 element element element element element

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

to6 :
Arr (Only Nat6) element
-> T6 element element element element element element

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

to7 :
Arr (Only Nat7) element
-> T7 element element element element element element element

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

to8 :
Arr (Only Nat8) element
-> T8
element
element
element
element
element
element
element
element

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

to9 :
Arr (Only Nat9) element
-> T9
element
element
element
element
element
element
element
element
element

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

Arr (Only Nat10) element
-> T10
element
element
element
element
element
element
element
element
element
element

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

Arr (Only Nat11) element
-> T11
element
element
element
element
element
element
element
element
element
element
element

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

Arr (Only Nat12) element
-> T12
element
element
element
element
element
element
element
element
element
element
element
element

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

Arr (Only Nat13) element
-> T13
element
element
element
element
element
element
element
element
element
element
element
element
element

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

Arr (Only Nat14) element
-> T14
element
element
element
element
element
element
element
element
element
element
element
element
element
element

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

combine

(a -> b -> combinedElement)
-> Arr length a
-> Arr length b
-> Arr length combinedElement

Combine the elements of 2 Arrs into new elements. If one list is longer, its extra elements are not used.

teamLives aBoard bBoard =
    Arr.map2 (\a b -> a.lives + b.lives)
        aBoard
        bBoard

The length of all Arrs must be in the same range.

  • If 1 Arr's maximum length isn't known:
Arr.map2 (\a b -> a.lives + b.lives)
    (aBoard |> Arr.toMin)
    (bBoard |> Arr.toMin)

aBoard : Arr (In Nat2 someMax_) Field
bBoard : Arr (In Nat2 otherMax_) Field
  • If 1 Arr has a higher minimum length:
Arr.map2 (\a b -> a.lives + b.lives)
    (aBoard |> Arr.lowerMinLength nat2)
    bBoard

aBoard : Arr (In Nat5 Nat10) Field
(a -> b -> c -> combinedElement)
-> Arr length a
-> Arr length b
-> Arr length c
-> Arr length combinedElement

Combine the elements of 3 Arrs into new elements. Works like map2.

(a -> b -> c -> d -> combinedElement)
-> Arr length a
-> Arr length b
-> Arr length c
-> Arr length d
-> Arr length combinedElement

Combine the elements of 4 Arrs into new elements. Works like map2.

drop information

Nat (ArgIn newMinLength minLength newMinIfN_)
-> Arr (In minLength maxLength) element
-> Arr (In newMinLength maxLength) element

Use a lower minimum length in the type.

[ atLeast3Elements
, atLeast4Elements
]

Elm complains:

But all the previous elements in the list are Arr (Min Nat3) ...

[ atLeast3Elements
, atLeast4Elements
    |> Arr.lowerMinLength nat3
]
toMin : Arr (In min max_) element -> Arr (Min min) element

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

between4And10Elements |> Arr.toMin
--> : Arr (Min Nat4) ...

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 Arr (Min Nat1) ...

[ atLeast1Element
, between1And10Elements |> Arr.toMin
]

restore

Nat (ArgIn max newMax ifN_)
-> Arr (In min max) element
-> Arr (In min newMax) element

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

While designing argument annotations as general as possible:

atMost18Elements : Arr (In min_ Nat18) ...

The argument in atMost18Elements should also fit in atMost19Elements.

atMost19Elements theArgument
--> error :(


atMost19Elements
    (theArgument |> Arr.restoreMaxLength nat18)
--> works

error

type alias Error =
{ expected : Expectation
, actual : { length : Nat (Min Nat0) }
}

An error for when a decoded array doesn't have the expected length.

You can transform it into a message with errorToString.

See the serialize functions.

= ExpectLength (Nat (Min Nat0))
| LengthInBound Expectation

An expectation for the decoded array that hasn't been met.

  • ExpectLength: expected a different exact length
  • LengthInBound: expected a length within a range (→ see InNat.Expectation)
    • ExpectAtLeast some minimum in a range
    • ExpectAtMost some maximum in a range

See errorToString and the serialize functions.

errorToString : Error -> String

Convert an error to a readable message.

{ expected = Arr.ExpectLength nat11
, actual = { length = nat10 }
}
    |> Arr.errorToString
--> "Expected an array of length 11, but the actual length was 10."

(example doesn't compile)