lue-bird/elm-emptiness-typed/4.1.0

NOTE: the latest version is 9.2.1

Fillable

An option-able value.

in arguments

import Fillable exposing (Empty)
import Stack exposing (StackFilled)


filling : Empty Never filling -> filling


top : Empty Never (StackFilled element) -> element

in type declarations

import Fillable exposing (Empty)
import Stack exposing (StackFilled)


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


type alias WithoutConstructorFunction record =
    record

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

type Empty possiblyOrNever filling
= Empty possiblyOrNever
| Filled filling

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

import Fillable exposing (Empty, filled)


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

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

import Fillable exposing (filling)


top : Empty Never (StackFilled element) -> element


Fillable.map Dict.NonEmpty.head
--: Empty possiblyOrNever (NonEmptyDict comparable v)
--: -> Empty possiblyOrNever ( comparable, v ) possiblyOrNever

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

create

empty : Empty Possibly filling_

Insert joke about life here.

empty
    |> Fillable.map (\x -> x / 0)
--> empty
filled : filling -> Empty never_ filling

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

import Fillable exposing (filled, filling)


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

Convert a Maybe to a Fillable.Empty.

transform

map :
(filling -> fillingMapped)
-> Empty possiblyOrNever filling
-> Empty possiblyOrNever fillingMapped

Change the possibly Filled value based on its current value:

import Fillable exposing (filled)


filled -3 |> Fillable.map abs
--> filled 3


Fillable.empty |> Fillable.map abs
--> Fillable.empty
(aFilling -> bFilling -> fillingCombined)
-> Empty possiblyOrNever aFilling
-> Empty possiblyOrNever bFilling
-> Empty possiblyOrNever fillingCombined

If all the arguments exist, combine them using a given function.

import Fillable exposing (filled, empty)


Fillable.map2 (+) (filled 3) (filled 4) --> filled 7
Fillable.map2 (+) (filled 3) empty --> empty
Fillable.map2 (+) empty (filled 4) --> empty
(filling -> Empty possiblyOrNever fillingThen)
-> Empty possiblyOrNever filling
-> Empty possiblyOrNever fillingThen

Chain together operations that may return empty.

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

Some call it flatMap.

emptiableString
    |> Fillable.andThen parse
    |> Fillable.andThen extraValidation


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

Safely extracts the value from an Empty Never.

import Fillable exposing (Empty, filled, filling)


filled (filled (filled "Bami"))
    |> filling
    |> filling
    |> filling
--> "Bami"


first : Empty Never ( first, others_ ) -> first
first =
    filling >> Tuple.first
(possiblyOrNever -> filling)
-> Empty possiblyOrNever filling
-> filling

Lazily use a fallback value if the Fillable.Empty empty.

import Possibly exposing (Possibly(..))
import Fillable exposing (toFillingOrIfEmpty)
import Dict


Dict.empty
    |> Dict.get "Hannah"
    |> Fillable.fromMaybe
    |> toFillingOrIfEmpty (\_ -> "unknown")
--> "unknown"


filling =
    toFillingOrIfEmpty never
toMaybe : Empty possiblyOrNever_ filling -> Maybe filling

Convert to a Maybe.

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

(filling -> value -> value)
-> Empty possiblyOrNever_ filling
-> value
-> value

If we have a Filled, alter the value using the filling.

import Fillable exposing (filled, ifFilled)
import Stack exposing (addOnTop)


Fillable.empty
    |> ifFilled addOnTop (filled 3)
    |> ifFilled addOnTop Fillable.empty
--> Stack.only 3
--: Empty Possibly (StackFilled number_)

If you dislike the argument positions, this might be for you:

import Fillable exposing (filled, ifFilled)
import Stack exposing (addOnTop)


Fillable.empty
    |> (filled 3 |> ifFilled addOnTop)
    |> (Fillable.empty |> ifFilled addOnTop)
--> Stack.only 3
--: Empty Possibly (StackFilled number_)

type-level

(possiblyOrNever -> possiblyOrNeverAdapted)
-> Empty possiblyOrNever filling
-> Empty possiblyOrNeverAdapted filling

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 Fillable exposing (Empty)
import Possibly exposing (Possibly)
import Stack exposing (StackFilled)


type alias Log =
    Empty Possibly (StackFilled String)


fromStack : Empty possiblyOrNever_ (StackFilled String) -> Log
fromStack stackFilled =
    stackFilled
        --: `possiblyOrNever_` but we need `Possibly`
        |> Fillable.adaptType (always Possible)

An argument or a type declaration value is Empty Never

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

import Fillable exposing (Empty)
import Stack exposing (StackFilled)


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


    else
        --: `Never` but we need `possiblyOrNever`
        aStack

to make both branches return possiblyOrNever, use

aStack |> Fillable.adaptType never