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 errorIs 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 maxThe 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.
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 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 noMake 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 yesCreate 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 yesOn 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.
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) CharRandom.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.randomIncreasing 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)))No elements
ArraySized.empty
--: ArraySized (In N0 atLeast0_) element
|> ArraySized.push ":)"
--: ArraySized (In N1 (Add1 atLeast0_)) StringCreate an ArraySized with exactly 1 element
Create an ArraySized with exactly 2 elements in this order
Create an ArraySized with exactly 3 elements in this order
Create an ArraySized with exactly 4 elements in this order
Create an ArraySized with exactly 5 elements in this order
Create an ArraySized with exactly 6 elements in this order
Create an ArraySized with exactly 7 elements in this order
Create an ArraySized with exactly 8 elements in this order
Create an ArraySized with exactly 9 elements in this order
Create an ArraySized with exactly 10 elements in this order
Create an ArraySized with exactly 11 elements in this order
Create an ArraySized with exactly 12 elements in this order
Create an ArraySized with exactly 13 elements in this order
Create an ArraySized with exactly 14 elements in this order
Create an ArraySized with exactly 15 elements in this order
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
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)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 )
--> 2Access its only value
ArraySized.l1 "hi" |> ArraySized.to1
--> "hi"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)
--> TrueWhether 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)
--> FalseSet 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" ]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 ]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" ]Equivalent to insert n0 Down. Put a new element after all the others.
between5And10Elements
|> ArraySized.push "becomes the last"
--: ArraySized (In N6 (Add11 a_)) StringPut a new element after the others.
atLeast5Elements
|> ArraySized.minPush "becomes the last"
--: ArraySized (Min N6) StringKick 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 )Kick out the element at an index in a direction.
removeLast =
TypeSized.minElementRemove ( Down, n0 )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' ]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 )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" ]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) StringTake 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 ]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.emptyFunnily, 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.phoneNumberElements 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_)) ...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) ...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.
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.
Split the ArraySized into equal-sized (except remainder) slices
in a direction
groups: the ArraySized divided into equal-sized Arrsless: 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 ]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 ]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.
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) ...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 n17AtLeastCompare 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 l8AtLeastIs 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 atLeast5Is 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 _ ->
NothingChange 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.fromCodeOh look, more type-safety!
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)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"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 ]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 ]Transform the ArraySized into a Toop.T2. This makes accessing elements and pattern matching easier.
Transform the ArraySized into a Toop.T3. This makes accessing elements and pattern matching easier.
Transform the ArraySized into a Toop.T4. This makes accessing elements and pattern matching easier.
Transform the ArraySized into a Toop.T5. This makes accessing elements and pattern matching easier.
Transform the ArraySized into a Toop.T6. This makes accessing elements and pattern matching easier.
Transform the ArraySized into a Toop.T7. This makes accessing elements and pattern matching easier.
Transform the ArraySized into a Toop.T8. This makes accessing elements and pattern matching easier.
Transform the ArraySized into a Toop.T9. This makes accessing elements and pattern matching easier.
Transform the ArraySized into a Toop.T10. This makes accessing elements and pattern matching easier.
Transform the ArraySized into a Toop.T11. This makes accessing elements and pattern matching easier.
Transform the ArraySized into a Toop.T12. This makes accessing elements and pattern matching easier.
Transform the ArraySized into a Toop.T13. This makes accessing elements and pattern matching easier.
Transform the ArraySized into a Toop.T14. This makes accessing elements and pattern matching easier.
Transform the ArraySized into a Toop.T15. This makes accessing elements and pattern matching easier.
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
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
]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)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
]
An
Arraywhere you know more about the amount of elements.result type
argument type
stored type
in your
Modelfor example (which means: no type variables)==crashes elm. Try other comparison methods