lue-bird/elm-typesafe-array

`Array` with a typed length range → safe access


Keywords
array, elm, range, type-safe, vector
License
MIT
Install
elm-package install lue-bird/elm-typesafe-array 25.0.0

Documentation

elm typesafe array

Knowing more about the length of an Array at compile-time to help you access elements safely

ticTacToeBoard
    |> ArraySized.element ( Up, n2 )
    |> ArraySized.element ( Up, n0 )

returns a value, not a Maybe if ticTacToeBoard's type can promise that it contains enough elements. Such a type could be:

type alias TicTacToeBoard =
    -- 3 by 3
    ArraySized (Exactly N3) (ArraySized (Exactly N3) Field)

type Field
    = Empty
    | X
    | O

ticTacToeBoard : TicTacToeBoard
ticTacToeBoard =
    ArraySized.l3
        (ArraySized.l3 Empty Empty O)
        (ArraySized.l3 Empty O Empty)
        (ArraySized.l3 O Empty Empty)

ticTacToeBoard
    |> ArraySized.element ( Up, n2 )
    |> ArraySized.element ( Up, n0 )
--→ O

We & the compiler knew there were enough elements in ticTacToeBoard

building blocks

elm install lue-bird/elm-linear-direction
elm install lue-bird/elm-bounded-nat
elm install lue-bird/elm-typesafe-array
import Linear exposing (Direction(..)) -- Up or Down
import N exposing (N, Exactly, Min, In, Up, To, Fixed)
import ArraySized exposing (ArraySized)

Let's define & use operations for ArraySizeds with a certain amount of elements ↓

a minimum length?

last :
    ArraySized (In (Fixed (Add1 minMinus1_)) max_) element
    -> element
last =
    ArraySized.element ( Down, n0 )

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

first ArraySized.empty -- compile-time error
greatest ArraySized.empty -- compile-time error

ArraySized (In (Fixed (Add1 minMinus1_)) max_) means what exactly? → It constrains the length of possible ArraySizeds

The types are explained in more detail in bounded-nat (In, Min, Exactly). In this example:

length is In a range

  • the minimum length constraint is, without adding anything, 1 + a variable (1 + 0 | 1 + 1 | 1 + ...) → Fixed (Add1 minMinus1_)
  • any maximum length constraint is allowed (even no maximum at all) → max_

an exact length?

Like in the tic-tac-toe example

import Linear exposing (Direction(..))
import N exposing (n1, n4, n6, n8, N8, Exactly)
import ArraySized exposing (ArraySized)

type alias ChessBoard =
    -- 8 by 8
    ArraySized (Exactly N8) (ArraySized (Exactly N8) Field)

type Field
    = Empty
    | Piece PieceKind Color

type PieceKind
    = Pawn
    | Other --...

type Color
    = Black
    | White

initialChessBoard : ChessBoard
initialChessBoard =
    let
        pawnRow color =
            ArraySized.repeat (Piece Pawn color) n8
        firstRow color =
            ArraySized.repeat (Piece Other color) n8
    in
    ArraySized.empty
        |> ArraySized.push (firstRow White)
        |> ArraySized.push (pawnRow White)
        |> ArraySized.glue Up
            (ArraySized.repeat (ArraySized.repeat Empty n8) n4)
        |> ArraySized.push (pawnRow Black)
        |> ArraySized.push (firstRow Black)

initialChessBoard
    |> ArraySized.element ( Up, n1 )
    |> ArraySized.element ( Up, n6 )
--> Piece Pawn White

a maximum length?

-- the max tag count should be 53
tag :
    ArraySized (In min_ (Up maxTo53_ To N53)) String
    -> (Metadata -> MetadataTagged)
tag tags =
    ...

tag (ArraySized.l3 "fun" "easy" "simple") -- valid
tag (ArraySized.repeat "into-the-trends" n100) -- type error

ready? go!

Orasund's static-array – comparison

typesafe-array development started before static-array was published but the ideas are really similar

create

  • static-array

    eleven =
        StaticArray.Length.ten |> StaticArray.Length.plus1
    
    StaticArray.fromList eleven 0 [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

    makes it easy to forget the length if you add a new element or remove one

    StaticArray.fromList eleven 0 [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]

    the added element 11 is simply ignored!

  • typesafe-array

    ArraySized.l11 0 1 2 3 4 5 6 7 8 9 10
    
    ArraySized.l11 0 1 2 3 4 5 6 7 8 9 10 11 -- type error

append

  • static-array

    staticArray1, staticArray2 : StaticArray Six ...
    
    let
        array1 =
            staticArray1 |> StaticArray.toRecord
        
        array2 =
            staticArray2 |> StaticArray.toRecord
    in
    StaticArray.fromRecord
        { length = StaticArray.Length.twelve
        , head = array1.head
        , tail = Array.append (array1.tail |> Array.push array2.head) array2.tail
        }

    important note from static-array:

    Notice that we can NOT do addition in compile time, therefore we need to construct 6+6 manually

    → You can easily do

    StaticArray.fromRecord
        { length = StaticArray.Length.eight
        , head = array1.head
        , tail = Array.empty
        }
        |> StaticArray.get
            (StaticArray.Length.eight |> StaticArray.Index.last)
    --→ array1.head

    The supplied length type doesn't match its actual length → we silently got back an element at the wrong (first) index!

  • typesafe-array

    arr1, arr2 :
        ArraySized (In (Up6 minX_) (Up6 maxX_)) ...
    
    arr1 |> ArraySized.glue Up arr2
    --: ArraySized (In (Up12 minX_) (Up12 maxX_)) ...

    type-safe

length in a range

  • static-array

    maybePush :
        Maybe element
        -> StaticArray length element
        -> ? -- what result type?
    
    type MaybePushResult lengthBefore element
        = Pushed
            (StaticArray    
                (StaticArray.Index.OnePlus lengthBefore)
                element
            )
        | DidntPush (StaticArray lengthBefore element)
    
    maybePush :
        Maybe element
        -> StaticArray length element
        -> MaybePushResult length element

    really inconvenient

  • typesafe-array

    pushMaybe :
        Maybe element
        -> ArraySized (In min (Up x To maxPlusX)) element
        -> ArraySized (In min (Up x To (Add1 maxPlusX))) element

anything static-array is better at?

  • separating length and index types
  • simple, easy to understand types