lue-bird / elm-emptiness-typed / Stack

📚 An emptiable or non-empty structure where top, removeTop, onTopLay are O(n)


type Stacked element
    = TopBelow (( element, List element ))

The representation of a non-empty stack on the Filled case. top, removeTop, onTopLay are O(n)

in arguments

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

top : Emptiable (Stacked element) Never -> element

in results

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

fromList : List element -> Emptiable (Stacked element) Possibly

We can treat it like any Emptiable:

import Emptiable exposing (Emptiable(..), filled, map)
import Possibly exposing (Possibly)
import Stack exposing (Stacked, top, onTopLay)

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

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

            Empty _ ->
                []

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

create

Emptiable.empty to create an Empty Possibly stack

one : element -> Emptiable (Stacked element) never_

A stack with just 1 single element

import Emptiable
import Stack exposing (onTopLay)

Stack.one ":)"
--> Emptiable.empty |> onTopLay ":)"

topBelow : element -> List element -> Emptiable (Stacked element) never_

A stack from a top element and a List of elements below

Stack.topBelow ":)" [ "wait", "a", "moment" ]
--> Stack.topBelow "wait" [ "a", "moment" ]
-->     |> onTopLay ":)"

fromTopBelow : ( element, List element ) -> Emptiable (Stacked element) never_

Take a tuple ( top, List belowElement ) from another source like turboMaCk/non-empty-list-alias and convert it to an Emptiable (StackTopBelow top belowElement) never_

Use topBelow if you don't already have a tuple to convert from

fromList : List element -> Emptiable (Stacked element) Possibly

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

import Possibly exposing (Possibly)
import Emptiable
import Stack exposing (topBelow)

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

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

When constructing from known elements, always prefer

import Stack exposing (topBelow)

topBelow "hello" [ "emptiness" ]

fromString : String -> Emptiable (Stacked Char) Possibly

Convert to an Emptiable (Stacked Char) Possibly. The Strings head becomes top, its tail is attachd below

import Possibly exposing (Possibly)
import Emptiable
import Stack exposing (topBelow)

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

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

When constructing from known elements, always prefer

import Stack exposing (topBelow)

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

fuzz : Fuzzer element -> Fuzzer (Emptiable (Stacked element) Possibly)

Emptiable (Stacked ...) Possibly Fuzzer. Generates stacks of varying length <= 32

import Stack exposing (topBelow)
import Fuzz

Stack.fuzz (Fuzz.intRange 0 9)
    |> Fuzz.examples 3
--> [ topBelow 2 [ 2, 5, 3, 8, 9, 4, 1, 0, 6, 6, 4, 7, 2, 6, 5 ]
--> , topBelow 8 [ 8, 0, 9, 8, 1, 0, 4, 1, 4, 6, 3, 4 ]
--> , topBelow 9 [ 7, 1, 5, 8, 2, 8, 3, 7, 4, 7 ]
--> ]
--: List (Emptiable (Stacked Int) Possibly)

filledFuzz : Fuzzer element -> Fuzzer (Emptiable (Stacked element) never_)

Emptiable Stacked Never Fuzzer. Generates stacks of varying length <= 32

import Stack exposing (topBelow)
import Fuzz

Stack.filledFuzz (Fuzz.intRange 0 9)
    |> Fuzz.examples 3
--> [ topBelow 4 [ 2, 2, 5, 3, 8, 9, 4, 1, 0, 6, 6, 4, 7, 2, 6, 5 ]
--> , topBelow 3 [ 4, 4, 5, 1, 7, 4, 2, 5, 6, 9, 7, 0, 1, 4, 1, 3, 2, 9, 6, 9, 0, 8, 3, 3, 3, 1, 5, 4, 9, 5, 2, 8 ]
--> , topBelow 5 [ 4, 9, 8, 9 ]
--> ]

scan

top : Emptiable (Stacked element) Basics.Never -> element

The first value

import Stack exposing (top, onTopLay)

Stack.one 3
    |> onTopLay 2
    |> top
--> 2

length : Emptiable (Stacked element_) possiblyOrNever_ -> Basics.Int

How many element there are

import Stack exposing (onTopLay)

Stack.one 3
    |> onTopLay 2
    |> Stack.length
--> 2

O(n) like List.length

alter

onTopLay : element -> Emptiable (Stacked element) possiblyOrNever_ -> Emptiable (Stacked element) never_

Add an element to the front

import Emptiable
import Stack exposing (topBelow, onTopLay)

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

Emptiable.empty |> onTopLay 1
--> Stack.one 1

removeTop : Emptiable (Stacked element) Basics.Never -> Emptiable (Stacked element) Possibly

Everything after the first element

import Stack exposing (topBelow)
import Linear exposing (Direction(..))

Stack.one 2
    |> Stack.onTopLay 3
    |> Stack.attach Down (topBelow 1 [ 0 ])
    |> Stack.removeTop
--> topBelow 0 [ 3, 2 ]
--: Emptiable (Stacked number_) Possibly

topAlter : (element -> element) -> Emptiable (Stacked element) possiblyOrNever -> Emptiable (Stacked element) possiblyOrNever

Change the first element based on its current value

import Stack

Stack.topBelow "Helpy IQ 4000 – the amazing vacuum cleaner"
    [ "faster and more thorough than ever seen before!" ]
    |> Stack.topAlter (\firstLine -> "Introducing: " ++ firstLine)
--> Stack.topBelow "Introducing: Helpy IQ 4000 – the amazing vacuum cleaner" [ "faster and more thorough than ever seen before!" ]

reverse : Emptiable (Stacked element) possiblyOrNever -> Emptiable (Stacked element) possiblyOrNever

Flip the order of the elements

import Stack exposing (topBelow)

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

filter

fills : Emptiable (Stacked (Emptiable element possiblyOrNever)) possiblyOrNever -> Emptiable (Stacked element) possiblyOrNever

Keep all filled elements and drop all empty elements

import Emptiable exposing (filled)
import Stack exposing (topBelow)

topBelow Emptiable.empty [ Emptiable.empty ]
    |> Stack.fills
--> Emptiable.empty

topBelow (filled 1) [ Emptiable.empty, filled 3 ]
    |> Stack.fills
--> topBelow 1 [ 3 ]

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

attaching

attach : Linear.Direction -> Emptiable (Stacked element) attachmentPossiblyOrNever_ -> Emptiable (Stacked element) possiblyOrNever -> Emptiable (Stacked element) possiblyOrNever

Glue the elements of a given stack to the end in a given direction of the stack

import Linear exposing (Direction(..))
import Stack exposing (topBelow)

topBelow 1 [ 2 ]
    |> Stack.attach Down (topBelow -1 [ 0 ])
--> topBelow -1 [ 0, 1, 2 ]

topBelow 1 [ 2 ]
    |> Stack.attach Down ([ -1, 0 ] |> Stack.fromList)
--> topBelow -1 [ 0, 1, 2 ]

Be aware:

Compared to attachAdapt

attachAdapt : Linear.Direction -> Emptiable (Stacked element) possiblyOrNever -> Emptiable (Stacked element) possiblyOrNeverIn_ -> Emptiable (Stacked element) possiblyOrNever

Glue the elements of a given stack to the end in a given direction of the stack, taking on the emptiness knowledge of the given stack

import Linear exposing (Direction(..))
import Emptiable
import Stack exposing (topBelow)

Emptiable.empty
    |> Stack.attachAdapt Down (topBelow 1 [ 2 ])
    |>  Stack.attachAdapt Down (topBelow -2 [ -1, 0 ])
--> topBelow -2 [ -1, 0, 1, 2 ]

Be aware:

Compared to attach

flatten : Emptiable (Stacked (Emptiable (Stacked element) possiblyOrNever)) possiblyOrNever -> Emptiable (Stacked element) possiblyOrNever

Glue together a bunch of stacks

import Emptiable
import Stack exposing (topBelow)

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

For this to return a filled stack, all stacks must be filled

transform

map : ({ index : Basics.Int } -> element -> elementMapped) -> Emptiable (Stacked element) possiblyOrNever -> Emptiable (Stacked elementMapped) possiblyOrNever

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

import Stack exposing (topBelow)

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

topBelow 1 [ 2, 3, 4 ]
    |> Stack.map (\{ index } n -> index * n)
--> topBelow 0 [ 2, 6, 12 ]

and : Emptiable (Stacked anotherElement) possiblyOrNever -> Emptiable (Stacked element) possiblyOrNever -> Emptiable (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 (topBelow)

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

topBelow 4 [ 5, 6 ]
    |> Stack.and (topBelow 1 [ 2, 3 ])
    |> Stack.map (\_ ( n0, n1 ) -> n0 + n1)
--> topBelow 5 [ 7, 9 ]

foldFrom : accumulationValue -> Linear.Direction -> (element -> accumulationValue -> accumulationValue) -> Emptiable (Stacked element) possiblyOrNever_ -> accumulationValue

Reduce in a direction

import Linear exposing (Direction(..))
import Stack exposing (topBelow)

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

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

Be aware:

foldFromOne : (element -> accumulated) -> Linear.Direction -> (element -> accumulated -> accumulated) -> Emptiable (Stacked element) Basics.Never -> accumulated

Fold, starting from one end element transformed to the initial accumulation value, then reducing what's accumulated in a given Direction

Usually used to convert to or operate on a different non-empty structure

stackFilledReverse =
    Stack.foldFromOne Stack.one Up Stack.onTopLay

-- module SetFilled exposing (SetFilled, fromStack, insert, one)

import Linear exposing (Direction(..))
import Emptiable exposing (Emptiable)
import Stack exposing (topBelow)
import Set exposing (Set)

fromStack : Emptiable (Stacked comparable) Never -> SetFilled comparable
fromStack =
    Stack.foldFromOne one Up insert

type alias SetFilled comparable =
    { anElement : comparable
    , otherElements : Set comparable
    }

one : comparable -> SetFilled comparable
one onlyElement =
    { anElement = onlyElement, otherElements = Set.empty }

insert : comparable -> (SetFilled comparable -> SetFilled comparable)
insert toInsert =
    \setFilled ->
        if toInsert == setFilled.anElement then
            setFilled
        else
            -- new element
            { setFilled
                | otherElements =
                    setFilled.otherElements |> Set.insert toInsert
            }

topBelow 3 [ 4, 5, 4, 3 ]
    |> fromStack
--> { anElement = 3, otherElements = Set.fromList [ 5, 4 ] }

(Know there's is something better than SetFilled: KeySet)

fold is a simple version that folds directly from the start element:

Stack.fold =
    Stack.foldFromOne identity

Be aware:

fold : Linear.Direction -> (element -> element -> element) -> Emptiable (Stacked element) Basics.Never -> element

Fold, starting from one end as the initial accumulation value, then reducing what's accumulated in a given Direction

import Linear exposing (Direction(..))
import Stack exposing (topBelow)

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

To fold into a different non-empty structure → Stack.foldFromOne

Be aware:

sum : Emptiable (Stacked number) possiblyOrNever_ -> number

∑ Total every element number

import Emptiable

topBelow 1 [ 2, 3 ] |> Stack.sum
--> 6

topBelow 1 (List.repeat 5 1) |> Stack.sum
--> 6

Emptiable.empty |> Stack.sum
--> 0

toTopBelow : Emptiable (Stacked element) Basics.Never -> ( element, List element )

Convert to a non-empty list tuple ( top, List belowElement ) to be used by another library

import Stack exposing (topBelow, toTopBelow)

topBelow "hi" [ "there", "👋" ]
    |> toTopBelow
--> ( "hi", [ "there", "👋" ] )

Don't use toTopBelow to destructure a stack. Instead: Stack.top, Stack.removeTop

toList : Emptiable (Stacked element) possiblyOrNever_ -> List element

Convert to a List

import Stack exposing (topBelow)

topBelow 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 : Emptiable (Stacked Char) possiblyOrNever_ -> String

Convert to a String

import Stack exposing (topBelow)

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

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