lue-bird/elm-emptiness-typed/1.0.0

NOTE: the latest version is 9.2.1

ListIs

types

type alias ListIs emptyOrNot a =
ListWithHeadType a emptyOrNot a

Describes an empty or non-empty list, making it more convenient than any Nonempty.

We can require a NotEmpty for example:

toNonempty : ListIs NotEmpty a -> Nonempty a

ListIs is equivalent to a MaybeIs of a non-empty list tuple:

MaybeIs emptyOrNot ( a, List a )

so we can treat it like a normal MaybeIs:

import MaybeIs exposing (MaybeIs(..))


ListIs.empty
--> MaybeIs.nothing


[ ... ]
    |> ListIs.fromList
    |> MaybeIs.map ListIs.head
--: MaybeIs Nothingable head_


toList : ListIs emptyOrNot_ a -> List a
toList list =
    case list of
        IsJust ( head_, tail_ ) ->
            head_ :: tail_


        IsNothing _ ->
            []
type alias Emptiable =
Nothingable { emptiable : () }

Emptiable marks lists that could be empty:

fromList : List a -> ListIs Emptiable a
fromList list =
    case list of
        [] ->
            ListIs.empty


        head :: tail ->
            ListIs.fromCons head tail
type alias NotEmpty =
Just { notEmpty : () }

NotEmpty can be used to require a non-empty list as an argument:

head : ListWithHeadType head NotEmpty tailElement_ -> head
type alias ListWithHeadType head emptyOrNot tailElement =
MaybeIs emptyOrNot ( head, List tailElement )

Describes an empty or non-empty list where the head type can be different from the tail element type.

Use ListIs if you have matching head and tail element types.

ListWithHeadType is the result of:

This is equivalent to a MaybeIs of a ( head, tail ) tuple:

import MaybeIs exposing (MaybeIs(..))


ListIs.empty
--> MaybeIs.nothing


MaybeIs.map ListIs.head
--: ListWithHeadType head emptyOrNot_ tailElement_
--: -> MaybeIs Nothingable head


tail : ListWithHeadType head_ NotEmpty tailElement -> List tailElement
tail listNotEmpty =
    case listNotEmpty of
        IsJust ( _, tailList ) ->
            tailList


        IsNothing _ ->
            []

create

empty : ListWithHeadType head_ Emptiable tailElement_

A ListIs without elements.

Equivalent to MaybeIs.nothing.

only : head -> ListWithHeadType head notEmpty_ tailElement_

A ListIs with just 1 element.

ListIs.only ":)"
--> ListIs.empty |> ListIs.cons ":)"
head
-> List tailElement
-> ListWithHeadType head notEmpty_ tailElement

Build a notEmpty_ from its head and tail.

( head, List tailElement )
-> ListWithHeadType head notEmpty_ tailElement

Convert a non-empty list tuple ( a, List b ) to a ListWithHeadType a notEmpty_ b.

Equivalent to MaybeIs.just.

fromList : List a -> ListIs Emptiable a

Convert a List a to a ListIs Emptiable a.

[] |> ListIs.fromList
--> ListIs.empty


[ "hello", "emptiness" ] |> ListIs.fromList
--> ListIs.fromCons "hello" [ "emptiness" ]
--: ListIs Emptiable String

When constructing from known elements, always prefer

ListIs.fromCons "hello" [ "emptiness" ]

scan

tail : ListWithHeadType head_ NotEmpty tailElement -> List tailElement

Everything after the first value in the ListIs.

ListIs.only 2
    |> ListIs.cons 3
    |> ListIs.append (ListIs.fromCons 1 [ 0 ])
    |> ListIs.tail
--> [ 2, 1, 0 ]
length : ListWithHeadType head_ emptyOrNot_ tailElement_ -> Int

How many element there are.

ListIs.only 3
    |> ListIs.cons 2
    |> ListIs.length
--> 2

modify

newHead
-> ListIs emptyOrNot_ a
-> ListWithHeadType newHead NotEmpty a

Add an element to the front of a list.

ListIs.fromCons 2 [ 3 ] |> ListIs.cons 1
--> ListIs.fromCons 1 [ 2, 3 ]


ListIs.empty |> ListIs.cons 1
--> ListIs.only 1

glue

ListIs appendedEmptyOrNot_ a
-> ListIs emptyOrNot a
-> ListIs emptyOrNot a

Glue the elements of a ListIs to the end of a ListIs.

ListIs.fromCons 1 [ 2 ]
    |> ListIs.append
        (ListIs.fromCons 3 [ 4 ])
--> ListIs.fromCons 1 [ 2, 3, 4 ]

Prefer this over appendNotEmpty if the piped ListIs is already known as NotEmpty or if both are Emptiable.

appendNotEmpty : ListIs NotEmpty a -> ListIs emptyOrNot_ a -> ListIs NotEmpty a

Glue the elements of a ListIs NotEmpty ... to the end of a ListIs.

ListIs.empty
    |> ListIs.appendNotEmpty
        (ListIs.fromCons 1 [ 2 ])
    |> ListIs.append
        (ListIs.fromCons 3 [ 4, 5 ])
--> ListIs.fromCons 1 [ 2, 3, 4, 5 ]

Prefer append if the piped ListIs is already known as NotEmpty or if both are Emptiable.

ListWithHeadType
(ListIs emptyOrNot a)
emptyOrNot
(ListIs tailListsEmptyOrNot_ a)
-> ListIs emptyOrNot a

Glue together a bunch of lists.

ListIs.fromCons
    (ListIs.fromCons 0 [ 1 ])
    [ ListIs.fromCons 10 [ 11 ]
    , ListIs.empty
    , ListIs.fromCons 20 [ 21, 22 ]
    ]
    |> ListIs.concat
--> ListIs.fromCons 0 [ 1, 10, 11, 20, 21, 22 ]

For this to return a ListIs notEmpty, there must be a non-empty first list.

filter

when : (a -> Bool) -> ListIs emptyOrNot_ a -> ListIs Emptiable a

Keep elements that satisfy the test.

ListIs.fromCons 1 [ 2, 5, -3, 10 ]
    |> ListIs.when (\x -> x < 5)
--> ListIs.fromCons 1 [ 2, -3 ]
--: ListIs Emptiable number_
whenJust : ListIs emptyOrNot_ (Maybe value) -> ListIs Emptiable value

Keep all Just values and drop all Nothings.

ListIs.fromCons (Just 1) [ Nothing, Just 3 ]
    |> ListIs.whenJust
--> ListIs.fromCons 1 [ 3 ]
--: ListIs Emptiable number


ListIs.fromCons Nothing [ Nothing ]
    |> ListIs.whenJust
--> ListIs.empty

transform

map : (a -> b) -> ListIs emptyOrNot a -> ListIs emptyOrNot b

Apply a function to every element.

ListIs.fromCons 1 [ 4, 9 ]
    |> ListIs.map negate
--> ListIs.fromCons -1 [ -4, -9 ]
(head -> mappedHead)
-> ListWithHeadType head emptyOrNot tailElement
-> ListWithHeadType mappedHead emptyOrNot tailElement

Apply a function to the head only.

ListIs.fromCons 1 [ 4, 9 ]
    |> ListIs.mapHead negate
--> ListIs.fromCons -1 [ 4, 9 ]
(tailElement -> mappedTailElement)
-> ListWithHeadType head emptyOrNot tailElement
-> ListWithHeadType head emptyOrNot mappedTailElement

Apply a function to every element of its tail.

ListIs.fromCons 1 [ 4, 9 ]
    |> ListIs.mapTail negate
--> ListIs.fromCons 1 [ -4, -9 ]
fold : LinearDirection -> (a -> acc -> acc) -> acc -> ListIs emptyOrNot_ a -> acc

Reduce a List in a direction.

import LinearDirection exposing (LinearDirection(..))


ListIs.fromCons 'l' [ 'i', 'v', 'e' ]
    |> ListIs.fold LastToFirst String.cons ""
--> "live"


ListIs.fromCons 'l' [ 'i', 'v', 'e' ]
    |> ListIs.fold FirstToLast String.cons ""
--> "evil"
LinearDirection
-> (tailElement -> acc -> acc)
-> ListWithHeadType acc NotEmpty tailElement
-> acc

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

import LinearDirection exposing (LinearDirection(..))


ListIs.fromCons 234 [ 345, 543 ]
    |> ListIs.foldWith FirstToLast max
--> 543
toList : ListIs emptyOrNot_ a -> List a

Convert the ListIs to a List.

ListIs.fromCons 1 [ 7 ]
    |> ListIs.toList
--> [ 1, 7 ]
ListWithHeadType head NotEmpty tailElement
-> ( head, List tailElement )

Convert a NotEmpty to a non-empty list tuple ( a, List a ).

Equivalent to MaybeIs.value.

(a -> b -> combined)
-> ListIs emptyOrNot a
-> ListIs emptyOrNot b
-> ListIs emptyOrNot combined

Combine 2 ListIss with a given function. If one list is longer, its extra elements are dropped.

ListIs.map2 (+)
    (ListIs.fromCons 1 [ 2, 3 ])
    (ListIs.fromCons 4 [ 5, 6, 7 ])
--> ListIs.fromCons 5 [ 7, 9 ]


ListIs.map2 Tuple.pair
    (ListIs.fromCons 1 [ 2, 3 ])
    ListIs.empty
--> ListIs.empty

For ListWithHeadType head ... tailElement where head and tailElement have a different type, there's map2HeadsAndTails.

(aHead -> bHead -> combinedHead)
-> (aTailElement -> bTailElement -> combinedTailElement)
-> ListWithHeadType aHead emptyOrNot aTailElement
-> ListWithHeadType bHead emptyOrNot bTailElement
-> ListWithHeadType combinedHead emptyOrNot combinedTailElement

Combine the head and tail elements of 2 ListIss using given functions. If one list is longer, its extra elements are dropped.

ListIs.map2HeadsAndTails Tuple.pair (+)
    (ListIs.fromCons "hey" [ 0, 1 ])
    (ListIs.fromCons "there" [ 1, 6, 7 ])
--> ListIs.fromCons ( "hey", "there" ) [ 1, 7 ]


ListIs.map2HeadsAndTails Tuple.pair (+)
    (ListIs.fromCons 1 [ 2, 3 ])
    ListIs.empty
--> ListIs.empty

For matching head and tailElement types, there's map2.