lue-bird/elm-emptiness-typed/2.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 (CanBe nothingTag_ ()) head_


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


        IsNothing _ ->
            []
type alias NotEmpty =
CanBe { empty : () } Never

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 (CanBe empty_ yesOrNever) tailElement_
--: -> MaybeIs (CanBe nothingTag_ yesOrNever) head


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


        IsNothing _ ->
            []

create

empty : ListWithHeadType head_ (CanBe empty_ ()) 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 (CanBe emptyTag_ ()) a

Convert a List a to a ListIs (CanBe emptyTag_ ()) a.

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


[ "hello", "emptiness" ] |> ListIs.fromList
--> ListIs.fromCons "hello" [ "emptiness" ]
--: ListIs (CanBe emptyTag_ ()) 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 can be empty.

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 can be empty.

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 (CanBe emptyTag_ ()) 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 (CanBe emptyTag_ ()) number_
ListWithHeadType
(MaybeIs emptyOrNot headValue)
emptyOrNot
(MaybeIs tailElementEmptyOrNot_ tailElementValue)
-> ListWithHeadType headValue emptyOrNot tailElementValue

Keep all just values and drop all Nothings.

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


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

As you can see, if the head is just a value, the result is NotEmpty.

transform

map :
(a -> b)
-> ListIs (CanBe empty_ yesOrNever) a
-> ListIs (CanBe mappedEmpty_ yesOrNever) b

Apply a function to every element.

ListIs.fromCons 1 [ 4, 9 ]
    |> ListIs.map negate
--> ListIs.fromCons -1 [ -4, -9 ]
(head -> mappedHead)
-> ListWithHeadType head (CanBe empty_ yesOrNever) tailElement
-> ListWithHeadType
mappedHead
(CanBe mappedEmpty_ yesOrNever)
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 (CanBe empty_ yesOrNever) tailElement
-> ListWithHeadType
head
(CanBe mappedEmpty_ yesOrNever)
mappedTailElement

Apply a function to every element of its tail.

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

Reduce a List in a direction.

import LinearDirection exposing (LinearDirection(..))


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


ListIs.fromCons 'l' [ 'i', 'v', 'e' ]
    |> ListIs.foldFrom "" 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.fold 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 (CanBe aEmpty_ yesOrNever) a
-> ListIs (CanBe bEmpty_ yesOrNever) b
-> ListIs (CanBe combinedEmpty_ yesOrNever) 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 (CanBe aEmpty_ yesOrNever) aTailElement
-> ListWithHeadType bHead (CanBe bEmpty_ yesOrNever) bTailElement
-> ListWithHeadType
combinedHead
(CanBe combinedEmpty_ yesOrNever)
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.