lue-bird/elm-typesafe-array/14.1.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:

0 to 100 joined by a space

joinBy between =
    \before after -> before ++ between ++ after


let
    intStringsAfter =
        Array.fromList (List.range 1 100)
            String.fromInt
in
Array.foldl (joinBy " ") "0" intStringsAfter
--> "0 1 2 3 4 5 ..."


Arr.nats nat100
    |> Arr.map (val >> String.fromInt)
    |> Arr.foldWith FirstToLast (joinBy " ")
--> "0 1 2 3 4 5 ..."

The Array version just seems hacky and is less readable. 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

For example to store in your Model

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

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

Tell the compiler if you know the amount of elements. Make sure the it knows as much as you!

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. Create an Arr from a List. As every List has >= 0 elements:

Arr.fromList listFromSomewhere
--> : Arr (Min Nat0)

Don't use it this way:

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

Tell the compiler if you know the amount of elements. Make sure the it knows as much as you!

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


Arr.nats nat7
--> big yes
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 (In Nat4 (Nat4Plus a)) Char




Arr.repeat atLeast3 'L'
--> : Arr (Min Nat3) Char
Nat (ArgIn minLength (Nat1Plus maxLengthMinus1) lengthIfN_)
-> Arr
(In minLength (Nat1Plus maxLengthMinus1))
(Nat (In Nat0 maxLengthMinus1))

Increasing natural numbers. In the end, there are length numbers.

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 dont know the maximum, e.g.

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

use minNats.

Nat (ArgIn minLength maxLength ifN_)
-> Arr (In minLength maxLength) (Nat (In Nat0 maxLength))

Increasing natural numbers. In the end, there are length numbers. Use nats if you know the maximum length.

Arr.nats nat5


Arr.nats between2And10

If not:

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


from first length =
    Arr.minNats length
        |> Arr.map (MinNat.add first)
Nat (ArgIn min max ifN_)
-> Generator element
-> Generator (Arr (In min max) 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)

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.length (Arr.from3 1 2 3)
--> nat3 : Nat (In Nat3 (Nat3Plus a_))


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


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

The element at a valid position in a direction.

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


Arr.from4 1 2 3 4
    |> Arr.at nat1 LastToFirst
--> 3

modify

Nat (ArgIn indexMin_ minLengthMinus1 indexIfN_)
-> LinearDirection
-> element
-> Arr (In (Nat1Plus minLengthMinus1) max) element
-> Arr (In (Nat1Plus minLengthMinus1) max) 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"
Nat (ArgIn indexMin_ minLengthMinus1 indexIfN_)
-> LinearDirection
-> (element -> element)
-> Arr (In (Nat1Plus minLengthMinus1) max) element
-> Arr (In (Nat1Plus minLengthMinus1) max) 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 10 20 -30
    |> Arr.updateAt nat0 LastToFirst (\x -> -x)
--> Arr.from3 10 20 30
reverse : Arr length element -> Arr length element

Flip the order of the elements.

Arr.from5 "l" "i" "v" "e"
    |> Arr.reverse
--> Arr.from5 "e" "v" "i" "l"
LinearDirection
-> Nat (ArgIn newMin newMax ifN_)
-> element
-> Arr length_ element
-> Arr (In newMin newMax) element

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

Arr.resize LastToFirst nat4 0
    (Arr.from2 1 2)
--> Array.from4 0 0 1 2


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


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


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

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

filter

(element -> Bool)
-> Arr (In min max) element
-> Arr (In Nat0 max) element

Only keep values that satisfy a test.

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

Remove values when 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_

slice

Nat (N taken atLeastTaken (Is takenToMin_ To min) is_)
-> LinearDirection
-> Arr (In min max_) 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 (In Nat3 Nat7) ...

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

Nat (N maxTaken atLeastMaxTaken (Is maxTakenToMin_ To min) is_)
-> Nat (ArgIn minTaken maxTaken takenIfN_)
-> LinearDirection
-> Arr (In min max_) 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 Nat7) ...

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 min_ max) element
-> { groups :
Arr
(In Nat0 max)
(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 which aren't enough
}


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 =
    (+) ('a' |> Char.toCode)
        >> Char.fromCode

val refers to Typed.val.

LinearDirection
-> (element -> result -> result)
-> result
-> Arr length_ element
-> result

Reduce an Arr in a direction.

Arr.from5 "l" "i" "v" "e"
    |> Arr.fold FirstToLast (++) ""
--> "live"


Arr.from5 "l" "i" "v" "e"
    |> Arr.fold LastToFirst (++) ""
--> "evil"
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 ]
LinearDirection
-> (element -> element -> element)
-> Arr (In (Nat1Plus minMinus1_) max_) element
-> element

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

Arr.foldWith FirstToLast maximum
    (Arr.from3 234 345 543)
--> 543


Arr.foldWith LastToFirst (++)
    (Arr.from3 "m" "l" "e")
--> "elm"

Often, calling map before foldWith is helpful.

Arr.map String.fromInt
    >> Arr.foldWith FirstToLast (++)
values : Arr (In min_ max) (Maybe value) -> Arr (In Nat0 max) value

Take every Just value & drop every Nothing.

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

Often, calling map before values is helpful. Many call this combination "filterMap".

Arr.from3 "1.2" "2" "hello"
    |> Arr.map String.toInt
    |> Arr.values
--> Arr.from1 2
--> : Arr (In Nat0 (Nat3Plus a_)) Int
toList : Arr length_ element -> List element

Short for Arr.toArray arr |> Array.toList. Convert the Arr to a List. Just do this in the end. Try to keep extra information as long as you can.

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

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.

teamLifes aBoard bBoard =
    Arr.map2 (\a b -> a.lifes + b.lifes)
        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.lifes + b.lifes)
    (aBoard |> MinArr.value)
    (bBoard |> MinArr.value)

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.lifes + b.lifes)
    (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 newMin min lowerIfN_)
-> Arr (In min max) element
-> Arr (In newMin max) 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
]

restore information

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

extra

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

A Codec to serialize Arrs with a specific amount of elements.

import Serialize




-- we can't start if we have no worlds to choose from!
serializeGameRow :
    Serialize.Codec
        String
        (Arr (Only Nat10) GameField)
serializeGameRow =
    Arr.serialize nat10 serializeGameField


encode : Arr (Only Nat10) GameField -> Bytes
encode =
    Serialize.encodeToBytes serializeGameRow


decode :
    Bytes
    ->
        Result
            (Serialize.Error String)
            (Arr (Only Nat10) GameField)
decode =
    Serialize.decodeFromBytes serializeGameRow