lue-bird/elm-emptiness-typed/5.1.0

NOTE: the latest version is 9.2.1

Stack

An emptiable or non-empty data structure where top, topRemove, onTopLay topMap are O(n).

type alias Stacked element =
StackTopBelow element element

A non-empty representation of a stack. top, topRemove, onTopLay topMap are O(n)

in arguments

Hand (StackFilled ...) Never Empty → stack is non-empty:

top : Hand (Stacked element) Never -> element

in results

Hand (StackFilled ...)Possibly Empty → stack could be empty

fromList : List element -> Hand (Stacked element) Possibly Empty

We can treat it like any Hand:

import Hand exposing (Hand(..), Empty, filled, fillMap)
import Possibly exposing (Possibly)
import Stack exposing (Stacked, top, onTopLay)


Hand.empty |> onTopLay "cherry" -- works


toList : Empty possiblyOrNever_ (Stacked element) -> List element
toList =
    \stack ->
        case stack of
            Filled (Stack.TopDown top_ down) ->
                top_ :: down


            Empty _ ->
                []


[ "hi", "there" ] -- comes in as an argument
    |> Stack.fromList
    |> fillMap (filled >> top)
--: Hand String Possibly Empty

Most operations also allow a different type for the top element → see StackTopBelow

type StackTopBelow topElement belowTopElement
= TopDown topElement (List belowTopElement)

Stacked with a different type for the top element:

top : Hand (StackTopBelow top belowTopElement_) Never Empty -> top

it's the result of:

create

Hand.empty to create an Empty Possibly stack.

only : top -> Hand (StackTopBelow top belowTopElement_) never_ Empty

A stack with just 1 single element.

import Hand
import Stack exposing (onTopLay)


Stack.only ":)"
--> Hand.empty |> onTopLay ":)"
top
-> List belowElement
-> Hand (StackTopBelow top belowElement) never_ Empty

A stack from a top element and a List of elements below going down.

( top, List belowElement )
-> Hand (StackTopBelow top belowElement) never_ Empty

Convert from a tuple ( top, List belowElement ).

Use topDown if you don't already have a tuple to convert from (for example coming from turboMaCk/non-empty-list-alias)

fromList : List element -> Hand (Stacked element) Possibly Empty

Convert a List element to a Empty Possibly (Stacked element). The Lists head becomes top, its tail is shoved down below.

import Possibly exposing (Possibly)
import Hand
import Stack exposing (topDown)


[] |> Stack.fromList
--> Hand.empty


[ "hello", "emptiness" ] |> Stack.fromList
--> topDown "hello" [ "emptiness" ]
--: Empty Possibly (Stacked String)

When constructing from known elements, always prefer

import Stack exposing (topDown)


topDown "hello" [ "emptiness" ]
fromString : String -> Hand (Stacked Char) Possibly Empty

Convert a String to a Hand (Stacked Char) Possibly Empty. The Strings head becomes top, its tail is shoved down below.

import Possibly exposing (Possibly)
import Hand
import Stack exposing (topDown)


"" |> Stack.fromString
--> Hand.empty


"hello" |> Stack.fromString
--> topDown 'h' [ 'e', 'l', 'l', 'o' ]
--: Hand (Stacked Char) Possibly Empty

When constructing from known elements, always prefer

import Stack exposing (topDown)


onTopLay 'h' ("ello" |> Stack.fromString)

scan

top : Hand (StackTopBelow top belowTop_) Never Empty -> top

The first value.

import Stack exposing (top, onTopLay)


Stack.only 3
    |> onTopLay 2
    |> top
--> 2
Hand (StackTopBelow top_ belowElement_) possiblyOrNever_ Empty
-> Int

How many element there are.

import Stack exposing (onTopLay)


Stack.only 3
    |> onTopLay 2
    |> Stack.length
--> 2
indexLast : Hand (StackTopBelow top_ belowElement_) Never Empty -> Int

The position of the element at the bottom.

import Stack exposing (onTopLay)


Stack.only 3
    |> onTopLay 2
    |> Stack.indexLast
--> 1

topRemove brings out everything below the top.

alter

newTop
-> Hand (Stacked belowElement) possiblyOrNever_ Empty
-> Hand (StackTopBelow newTop belowElement) never_ Empty

Add an element to the front.

import Hand
import Stack exposing (topDown, onTopLay)


topDown 2 [ 3 ] |> onTopLay 1
--> topDown 1 [ 2, 3 ]


Hand.empty |> onTopLay 1
--> Stack.only 1
Hand (StackTopBelow top_ belowElement) Never Empty
-> Hand (Stacked belowElement) Possibly Empty

Everything after the first value.

import Stack exposing (topDown, onTopLay, onTopStack, topRemove)


Stack.only 2
    |> onTopLay 3
    |> onTopStack (topDown 1 [ 0 ])
    |> topRemove
--> topDown 0 [ 3, 2 ]
--: Hand (Stacked number_) Possibly Empty
Hand (Stacked element) possiblyOrNever Empty
-> Hand (Stacked element) possiblyOrNever Empty

Flip the order of the elements.

import Stack exposing (topDown)


topDown "l" [ "i", "v", "e" ]
    |> Stack.reverse
--> topDown "e" [ "v", "i", "l" ]

filter

(Hand topContent possiblyOrNever Empty)
(Hand belowElementContent belowPossiblyOrNever_ Empty)
)
possiblyOrNever
-> Hand
(StackTopBelow topContent belowElementContent)
possiblyOrNever

Keep all filled elements and drop all empty elements.

import Hand exposing (filled)
import Stack exposing (topDown)


topDown Hand.empty [ Hand.empty ]
    |> Stack.fills
--> Hand.empty


topDown (filled 1) [ Hand.empty, filled 3 ]
    |> Stack.fills
--> topDown 1 [ 3 ]

As you can see, if only the top is fill a value, the result is non-empty.

glue

List element
-> Hand (Stacked element) possiblyOrNever Empty
-> Hand (Stacked element) possiblyOrNever Empty

Put the elements of a List on top.

import Stack exposing (topDown, onTopStack)


topDown 1 [ 2 ]
    |> Stack.onTopGlue [ -1, 0 ]
--> topDown -1 [ 0, 1, 2 ]

onTopGlue only when the piped stack food is already ... Never Empty. Glue a stack on top with

  • onTopStack takes on the possiblyOrNever Empty type of the piped food
  • onTopStackAdapt takes on the possiblyOrNever Empty type of the argument
Hand (Stacked element) possiblyOrNeverAppended_ Empty
-> Hand (Stacked element) possiblyOrNever Empty
-> Hand (Stacked element) possiblyOrNever Empty

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

import Stack exposing (topDown, onTopStack)


topDown 1 [ 2 ]
    |> onTopStack (topDown -1 [ 0 ])
--> topDown -1 [ 0, 1, 2 ]
  • onTopStack takes on the possiblyOrNever Empty type of the piped food
  • onTopStackAdapt takes on the possiblyOrNever Empty type of the argument
Hand (Stacked element) possiblyOrNever Empty
-> Hand (Stacked element) possiblyOrNeverIn_ Empty
-> Hand (Stacked element) possiblyOrNever Empty

Glue the elements of a ... Empty possiblyOrNever/Never Stack to the top of this Stack.

import Hand
import Stack exposing (topDown, onTopStack, onTopStackAdapt)


Hand.empty
    |> onTopStackAdapt (topDown 1 [ 2 ])
    |> onTopStack (topDown -2 [ -1, 0 ])
--> topDown -2 [ -1, 0, 1, 2 ]
  • onTopStack takes on the possiblyOrNever Empty type of the piped food
  • onTopStackAdapt takes on the possiblyOrNever Empty type of the argument
(Hand (Stacked element) possiblyOrNever Empty)
(Hand (Stacked element) belowTopListsPossiblyOrNever_ Empty)
)
possiblyOrNever
-> Hand (Stacked element) possiblyOrNever Empty

Glue together a bunch of stacks.

import Hand
import Stack exposing (topDown)


topDown
    (topDown 0 [ 1 ])
    [ topDown 10 [ 11 ]
    , Hand.empty
    , topDown 20 [ 21, 22 ]
    ]
    |> Stack.flatten
--> topDown 0 [ 1, 10, 11, 20, 21, 22 ]

For this to return a non-empty stack, there must be a non-empty top stack.

transform

map :
({ index : Int } -> element -> elementMapped)
-> Hand (Stacked element) possiblyOrNever Empty
-> Hand (Stacked elementMapped) possiblyOrNever Empty

Change every element based on its current value and { index }.

import Stack exposing (topDown)


topDown 1 [ 4, 9 ]
    |> Stack.map (\_ -> negate)
--> topDown -1 [ -4, -9 ]


topDown 1 [ 2, 3, 4 ]
    |> Stack.map (\{ index } n -> index * n)
--> topDown 0 [ 2, 6, 12 ]
and :
Hand (Stacked anotherElement) possiblyOrNever Empty
-> Hand (Stacked element) possiblyOrNever Empty
-> Hand
(Stacked ( element, anotherElement ))
possiblyOrNever

Combine its elements with elements of a given stack at the same location. If one stack is longer, the extra elements are dropped.

import Stack exposing (topDown)


topDown "alice" [ "bob", "chuck" ]
    |> Stack.and (topDown 2 [ 5, 7, 8 ])
--> topDown ( "alice", 2 ) [ ( "bob", 5 ), ( "chuck", 7 ) ]


topDown 4 [ 5, 6 ]
    |> Stack.and (topDown 1 [ 2, 3 ])
    |> Stack.map (\_ ( n0, n1 ) -> n0 + n1)
--> topDown 5 [ 7, 9 ]
(top -> topMapped)
-> Hand (StackTopBelow top belowElement) possiblyOrNever Empty
-> Hand
(StackTopBelow topMapped belowElement)
possiblyOrNever

Change the top element based on its current value. Its type is allowed to change.

import Stack exposing (topDown, topMap)


topDown 1 [ 4, 9 ]
    |> topMap negate
--> topDown -1 [ 4, 9 ]
({ index : Int } -> belowElement -> belowElementMapped)
-> Hand (StackTopBelow top belowElement) possiblyOrNever Empty
-> Hand
(StackTopBelow top belowElementMapped)
possiblyOrNever

Change every element below its top based on their { index } in the whole stack and their current value. Their type is allowed to change.

import Stack exposing (topDown, belowTopMap)


topDown 1 [ 4, 9 ]
    |> belowTopMap (\_ -> negate)
--> topDown 1 [ -4, -9 ]
( accumulationValue
, DirectionLinear
, element -> accumulationValue -> accumulationValue
)
-> Hand (Stacked element) possiblyOrNever_ Empty
-> accumulationValue

Reduce in a direction.

import Linear exposing (DirectionLinear(..))
import Stack exposing (topDown)


topDown 'l' [ 'i', 'v', 'e' ]
    |> Stack.foldFrom ( "", Down, String.cons )
--> "live"


topDown 'l' [ 'i', 'v', 'e' ]
    |> Stack.foldFrom ( "", Up, String.cons )
--> "evil"

Be aware:

  • Down = indexes decreasing, not: from the top down
  • Up = indexes increasing, not: from the bottom up
(belowElement -> top -> top)
-> Hand (StackTopBelow top belowElement) Never Empty
-> top

Fold from the top as the initial accumulation value.

import Linear exposing (DirectionLinear(..))
import Stack exposing (topDown)


topDown 234 [ 345, 543 ]
    |> Stack.fold max
--> 543

fold doesn't take a direction as an argument because at the time of writing, the best implementation would include a Stack.reverse.

Hand (StackTopBelow top belowElement) Never Empty
-> ( top, List belowElement )

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

Currently equivalent to fill.

import Stack exposing (topDown, toTopDown)


topDown "hi" [ "there", "👋" ]
    |> toTopDown
--> ( "hi", [ "there", "👋" ] )
toList : Hand (Stacked element) possiblyOrNever_ Empty -> List element

Convert it to a List.

import Stack exposing (topDown)


topDown 1 [ 7 ] |> Stack.toList
--> [ 1, 7 ]

Don't try to use this prematurely. Keeping type information as long as possible is always a win.

toString : Hand (Stacked Char) possiblyOrNever_ Empty -> String

Convert it to a String.

import Stack exposing (topDown)


topDown 'H' [ 'i' ] |> Stack.toString
--> "Hi"

Don't try to use this prematurely. Keeping type information as long as possible is always a win.