lue-bird/elm-allowable-state/1.0.0

NOTE: the latest version is 1.0.2

allow/forbid a state at the type level

allowable state

There are many types that promise non-emptiness. One example: MartinSStewart's NonemptyString.

fromInt, char, ... promise to return a filled string at compile-time.

→ head, tail, ... are guaranteed to succeed. No Maybes have to be carried throughout your program. Cool.

How about operations that work on non-empty and emptiable strings, like

length : Text canBeNonEmptyOrEmptiable -> Int

toUpper :
    Text canBeNonEmptyOrEmptiable
    -> Text canBeNonEmptyOrEmptiable
...

or ones that can pass the (im)possibility of a state from one data structure to the other?

toChars :
    Text nonEmptyOrEmptiable
    -> Stack Char nonEmptyOrEmptiable

All this is very much possible 🔥

Let's experiment and see where we end up.

type TextThatCan unitOrNever beEmpty
    = TextEmpty unitOrNever
    | TextFilled Char String

type BeEmpty
    = BeEmptyTag Never

char : Char -> TextThatCan Never BeEmpty
char onlyChar =
    TextFilled onlyChar ""

top : TextThatCan Never BeEmpty -> Char
top =
    \text ->
        case text of
            TextFilled headChar _ ->
                headChar
            
            TextEmpty possiblyOrNever ->
                possiblyOrNever |> never --! neat

top (char 'E') -- 'E'
top (TextEmpty ()) -- error

→ The type TextThatCan Never BeEmpty limits arguments to just TextFilled.

Lets make the type TextThatCan ()/Never BeEmpty handier:

type TextEmpty possiblyOrNever

type alias Possibly =
    ()

empty : TextEmpty Possibly
top : TextEmpty Never -> Char

To avoid misuse like empty : Text () Empty, we'll represent the () tag as a type:

type Possibly
    = Possible

top : Text Never Empty -> Char

empty : Text Possibly Empty
empty =
    TextEmpty Possible

👌. Now the fun part: Carrying emptiness-information over:

toChars :
    TextEmpty possiblyOrNever
    -> StackEmpty possiblyOrNever Char
toChars string =
    case string of
        TextEmpty possiblyOrNever ->
            StackEmpty possiblyOrNever

        TextFilled headChar tailString ->
            StackFilled headChar (tailString |> String.toList)

so

TextEmpty Never -> StackEmpty Never Char
TextEmpty Possibly -> StackEmpty Possibly Char

I hope you got the idea: You can allow of forbid a variant by adding a type argument that is either Never or Possibly

Take a look at data structures that build on this idea. They really make life easier.