lue-bird/elm-emptiness-typed/5.1.0

NOTE: the latest version is 9.2.1

Hand

An emptiable-able value.

in arguments

import Hand exposing (Empty)
import Stack exposing (Stacked)


fill : Hand fill Never Empty -> fill


top : Hand (Stacked element) Never Empty -> element

in type declarations

import Hand exposing (Empty, Hand)
import Stack exposing (Stacked)


type alias Model =
    WithoutConstructorFunction
        { searchKeyWords : Hand (Stacked String) Never Empty
        }

where RecordWithoutConstructorFunction stops the compiler from creating a constructor function for Model.

type Hand fill possiblyOrNever emptyTag
= Empty possiblyOrNever
| Filled fill

Like Maybe, but able to know at type-level whether Empty is possible.

import Hand exposing (Hand, Empty, filled, fill)


[ filled 1, filled 7 ]
    --: List (Hand number_ never_ Empty)
    |> List.map fill
--> [ 1, 7 ]

Hand.Empty alone probably won't be that useful, but it can make data structures type-safely non-emptiable:

import Hand exposing (fillMap)


top : Hand (Stacked element) Never Empty -> element


fillMap Dict.NonEmpty.head
--: Hand (NonEmptyDict comparable v) possiblyOrNever Empty
--: -> Hand ( comparable, v ) possiblyOrNever Empty

Go take a look at all the data structures in this package.

type Empty

A word used in the Hand type:

top : Hand (Stacked element) Never Empty -> element


when :
    Hand ... possiblyOrNever_ Empty
    -> Hand ... Possibly Empty

create

empty : Hand filling_ Possibly Empty

Insert joke about life here.

Hand.empty
    |> Hand.fillMap (\x -> x / 0)
--> Hand.empty
filled : fill -> Hand fill never_ Empty

A Hand.Empty that certainly exists, allowing type-safe extraction.

import Hand exposing (filled, fill)


filled "Bami" |> fill
--> "Bami"
fromMaybe : Maybe value -> Hand value Possibly Empty

Convert a Maybe to a HandPossiblyHand.

To create new Hands, use filled and empty instead!

transform

(fill -> fillMapped)
-> Hand fill possiblyOrNever Empty
-> Hand fillMapped possiblyOrNever Empty

If the Hand is filled, change it based on its current fill:

import Hand exposing (filled, fillMap)


filled -3 |> fillMap abs
--> filled 3


Hand.empty |> fillMap abs
--> Hand.empty
(fill -> Hand fillIfBothFilled possiblyOrNever Empty)
-> Hand fill possiblyOrNever Empty
-> Hand fillIfBothFilled possiblyOrNever Empty

Chain together operations that may return empty. It's like calling fillMap|>flatten:

If the argument Hand.Empty Never Empty, a given function takes its fill and returns a new possibly empty value.

Some call it andThen or flatMap.

import Hand exposing (Hand, Empty, fillMapFlat)


emptiableString
    |> fillMapFlat parse
    |> fillMapFlat extraValidation


parse : ( Char, String ) -> Hand Parsed Possibly Empty
extraValidation : Parsed -> Hand Parsed Possibly Empty

For any number of arguments: fillAnd... |> ... |>fillMapFlat:

import Hand exposing (filled, fillMapFlat, fillAnd)


(filled 3)
    |> fillAnd (filled 4)
    |> fillAnd (filled 5)
    |> fillMapFlat
        (\( ( a, b ), c ) -> filled ( a, b, c ))
--> filled ( 3, 4, 5 )
Hand anotherFill possiblyOrNever Empty
-> Hand fill possiblyOrNever Empty
-> Hand ( fill, anotherFill ) possiblyOrNever Empty

If the incoming food and the given argument are filled, give a filled tuple of both fills back.

If any is empty, give a Hand.empty back.

fillAnd comes in handy when multiple arguments need to be filled:

import Hand exposing (filled, fillMap, fillAnd)


filled 3
    |> fillAnd (filled 4)
    |> fillAnd (filled 5)
    |> fillMap (\( ( a0, a1 ), a2 ) -> a0^a1 - a2^2)
--> filled 56
Hand (Hand fill possiblyOrNever Empty) possiblyOrNever Empty
-> Hand fill possiblyOrNever Empty

In a nestable Hand: Only keep it filled if the inner Hand is filled.

Some call it join.

import Hand exposing (filled)


filled (filled 1) |> Hand.flatten
--> filled 1


filled Hand.empty |> Hand.flatten
--> Hand.empty


Hand.empty |> Hand.flatten
--> Hand.empty
fill : Hand fill Never Empty -> fill

Safely extract the filled content from a Hand fill Never Empty.

import Hand exposing (Empty, filled)


filled (filled (filled "Bami"))
    |> Hand.fill
    |> Hand.fill
    |> Hand.fill
--> "Bami"


first : Empty ( first, others_ ) Never Empty -> first
first =
    Hand.fill >> Tuple.first
fillElseOnEmpty : (possiblyOrNever -> fill) -> Hand fill possiblyOrNever Empty -> fill

Lazily use a fallback value if the Hand is empty.

import Possibly exposing (Possibly(..))
import Hand exposing (Hand(..), fillElseOnEmpty)
import Dict


Dict.empty
    |> Dict.get "Hannah"
    |> Hand.fromMaybe
    |> fillElseOnEmpty (\_ -> "unknown")
--> "unknown"


fill =
    fillElseOnEmpty never


fatten =
    fillElseOnEmpty Empty
toMaybe : Hand fill possiblyOrNever_ Empty -> Maybe fill

Convert to a Maybe.

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

type-level

(possiblyOrNever -> adaptedPossiblyOrNever)
-> Hand fill possiblyOrNever Empty
-> Hand fill adaptedPossiblyOrNever Empty

Change the possiblyOrNever type.

Returning a type declaration value or argument that is Empty Possibly

An Empty possiblyOrNever can't be used as Empty Possibly

import Hand exposing (Empty)
import Possibly exposing (Possibly)
import Stack exposing (Stacked)


type alias Log =
    Empty Possibly (Stacked String)


fromStack : Hand (Stacked String) possiblyOrNever_ Empty -> Log
fromStack stackFilled =
    stackFilled
        --: `possiblyOrNever_` but we need `Possibly`
        |> Hand.emptyAdapt (always Possible)

An argument or a type declaration value is Never Empty

The Never Empty can't be unified with Possibly Empty or a type variable

import Hand exposing (Empty)
import Stack exposing (Stacked)


theShorter :
    Hand (Stacked element) Never Empty
    -> Hand (Stacked element) possiblyOrNever Empty
    -> Hand (Stacked element) possiblyOrNever Empty
theShorter aStack bStack =
    if Stack.length bStack > Stack.length aStack then
        bStack


    else
        aStack
            --: `Never` but we need `possiblyOrNever`
            |> Hand.emptyAdapt never

makes both branches return possiblyOrNever.