edkelly303/elm-any-type-forms/8.0.0

NOTE: the latest version is 8.1.0

Control

Creating a form

type alias Control context state delta output =
AdvancedControl context output state delta output

A Control is the basic unit from which forms are composed. This library provides various example controls and combinators that you can use to build a form for any arbitrarily complex Elm type.

type alias Form state delta output msg =
{ blank : ( State state, Cmd msg )
, prefill : output -> ( State state, Cmd msg )
, update : Delta delta -> State state -> ( State state, Cmd msg )
, view : State state -> Html msg
, subscriptions : State state -> Sub msg
, submit :
State state -> ( State state, Result (List Feedback) output )
}

A Form is a record which contains functions that you can use in your Elm Program to initialise, view, update, subscribe, and submit your form.

{ outputToString : output -> String
, control : Control () state delta output
}
-> Program () (State state) (Delta delta)

Test and debug a Control by turning it into a Program that you can run as a standalone Elm application.

import Control exposing (sandbox, int, State, Delta)


sandbox
    { control = int
    , outputToString = Debug.toString
    }


--: Program () (State String) (Delta String)

This is useful when you're rapidly iterating on designing a form, and you don't yet want the hassle of plumbing it into your main Elm application's Modeland Msg types.

Once you're happy with the form, you can then ask the Elm repl (or your editor's Elm plugin) to tell you the type signature of the Program, which will give you the form's State and Delta types. You can then plug these into your main Modeland Msg types wherever appropriate.

{ control : Control () state delta output
, onUpdate : Delta delta -> msg
, onSubmit : msg
}
-> Form state delta output msg

Convert a Control into a simple Form, rendered as a list of controls wrapped in an HTML <form> element, with a submit button at the bottom.

You will need to supply a couple of variants from your app's Msg type: one to handle updates of the form's state, and one to handle the submission of the form, as follows:

import Control exposing (Form, Delta, simpleForm, int)


type Msg
    = FormUpdated (Delta String)
    | FormSubmitted


simpleForm
    { control = int
    , onUpdate = FormUpdated
    , onSubmit = FormSubmitted
    }


--: Form String String Int Msg

Now you can integrate your Form into a larger Elm app:

  1. Call mySimpleForm.blank or mySimpleForm.prefill in your init function.

  2. Call mySimpleForm.update in the FormUpdated branch of your update function.

  3. Call mySimpleForm.submit in the FormSubmitted branch of your update function.

  4. Call mySimpleForm.view in your view function.

  5. Call mySimpleForm.subscriptions in your subscriptions function.

{ control : Control () state delta output
, onUpdate : Delta delta -> msg
, view : List (Html msg) -> Html msg
}
-> Form state delta output msg

Convert a Control into a Form, and render the form however you like.

You will need to supply a variant from your app's Msg type to handle updates of the form's state. You will also need to supply a view function that takes the List (Html msg) produced by the supplied Control and returns an Html msg, as follows:

import Control exposing (Form, Delta, form, int)
import Html
import Html.Attributes
import Html.Events


type Msg
    = FormUpdated (Delta String)
    | FormSubmitted


form
    { control = int
    , onUpdate = FormUpdated
    , view =
        \controlView ->
            Html.form
                [ Html.Events.onSubmit FormSubmitted ]
                (controlView ++ [ Html.button [ Html.Attributes.type_ "submit" ] [ Html.text "Submit" ] ])
    }


--: Form String String Int Msg

Now you can integrate your Form into a larger Elm app:

  1. Call myForm.blank or myForm.prefill in your init function.

  2. Call myForm.update in the FormUpdated branch of your update function.

  3. Call myForm.submit in the FormSubmitted branch of your update function.

  4. Call myForm.view in your view function.

  5. Call myForm.subscriptions in your subscriptions function.

Basic controls

bool : Control context Bool Bool Bool

A control that produces a Bool. Renders as an HTML checkbox. Default output is False.

import Control exposing (Control, bool)


bool


--: Control context Bool Bool Bool
int : Control context String String Int

A control that produces an Int. Renders as an HTML number input.

import Control exposing (Control, int)


int


--: Control context String String Int
float : Control context String String Float

A control that produces a Float. Renders as an HTML number input.

import Control exposing (Control, float)


float


--: Control context String String Float
string : Control context String String String

A control that produces a String. Renders as an HTML text input.

import Control exposing (Control, string)


string


--: Control context String String String
char : Control context String String Char

A control that produces a Char. Renders as an HTML text input.

import Control exposing (Control, char)


char


--: Control context String String Char
( String, enum )
-> ( String, enum )
-> List ( String, enum )
-> Control context enum enum enum

A control that produces a custom type where none of the variants have any arguments. Renders as an HTML radio input. Defaults to the first variant.

import Control exposing (Control, enum)


type Colour
    = Red
    | Green
    | Blue


enum
    ( "Red", Red )
    ( "Green", Green )
    [ ( "Blue", Blue ) ]


--: Control context Colour Colour Colour

Basic combinators

type Tuple fst snd

A type used to contain the states or deltas of a tuple control.

Control context state1 delta1 output1
-> Control context state2 delta2 output2
-> Control
context
(Tuple state1 state2)
(Tuple delta1 delta2)
( output1, output2 )

A combinator that produces a tuple of two controls of given types.

import Control exposing (Control, Tuple, tuple, string, bool)


tuple string bool


--: Control context (Tuple String Bool) (Tuple String Bool) ( String, Bool )
type Triple a b c

A type used to contain the states or deltas of a triple control.

Control context state1 delta1 output1
-> Control context state2 delta2 output2
-> Control context state3 delta3 output3
-> Control
context
(Triple state1 state2 state3)
(Triple delta1 delta2 delta3)
( output1, output2, output3 )

A combinator that produces a triple of three controls of given types.

import Control exposing (Control, Triple, triple, string, bool, float)


triple string bool float


--: Control context (Triple String Bool String) (Triple String Bool String) ( String, Bool, Float )
type Maybe_ a

A type used to contain the state or delta types for a maybe control.

Control context state delta output
-> Control context (Maybe_ state) (Maybe_ delta) (Maybe output)

A combinator that produces a Maybe of a control of a given type.

import Control exposing (Control, Maybe_,  int, maybe)


maybe int


--: Control context (Maybe_ String) (Maybe_ String) (Maybe Int)
type Result_ err ok

A type used to contain the state or delta types for a result control.

Control context errState errDelta errOutput
-> Control context okState okDelta okOutput
-> Control
context
(Result_ errState okState)
(Result_ errDelta okDelta)
(Result errOutput okOutput)

A combinator that produces a Result with a given error and success type.

import Control exposing (Control, Result_, result, string, bool)


result string bool


--: Control context (Result_ String Bool) (Result_ String Bool) (Result String Bool)
type List_ a

A type used to contain the state or delta of a list control.

Control context state delta output
-> Control context (List_ state) (List_ delta) (List output)

A combinator that produces a List of controls of a given type.

import Control exposing (Control, List_, list, bool)


list bool


--: Control context (List_ Bool) (List_ Bool) (List Bool)
type Dict_ k v

A type used to contain the state or delta of a dict control.

Control context keyState keyDelta comparable
-> Control context valueState valueDelta value
-> Control
context
(Dict_ keyState valueState)
(Dict_ keyDelta valueDelta)
(Dict comparable value)

A combinator that produces a Dict from controls of given key and value types. The key type must be comparable.

import Control exposing (Control, Dict_, dict, string, bool)
import Dict


dict string bool


--: Control context (Dict_ String Bool) (Dict_ String Bool) (Dict.Dict String Bool)
type Set_ a

A type used to contain the state or delta of a set control.

set :
Control context state delta comparable
-> Control context (Set_ state) (Set_ delta) (Set comparable)

A combinator that produces a Set from controls of a given member types. The member type must be comparable.

import Control exposing (Control, Set_, set, string)
import Set


set string


--: Control context (Set_ String) (Set_ String) (Set.Set String)
type Array_ a

A type used to contain the state or delta of an array control.

Control context state delta output
-> Control context (Array_ state) (Array_ delta) (Array output)

A combinator that produces an Array from a control of a given type.

import Control exposing (Control, Array_, array, int)
import Array


array int


--: Control context (Array_ String) (Array_ String) (Array.Array Int)
type Mapping a

A type used to contain the state or delta type for a control whose output has been converted using map.

map :
{ convert : a -> b
, revert : b -> a
}
-> Control context state delta a
-> Control context (Mapping state) (Mapping delta) b

A combinator that converts a Control whose output is of type a to a Control whose output is of type b.

This is particularly useful for implementing "wrapper" types, such as Ids.

Note: with most common map functions, such as List.map, we only need to supply a function from a -> b. In this case, however, we need to supply two functions that will allow us to both convert the type from a -> b and revert it back from b -> a.

import Control exposing (Control, Mapping, int, map)


type Id
    = Id Int


int
    |> map
        { convert = \i -> Id i
        , revert = \(Id i) -> i
        }


--: Control context (Mapping String) (Mapping String) Id

Building record combinators

type Record record

A type used to contain the state or delta of a record control.

type Field field restFields

A type used to contain the state or delta of a field within a record control.

A type used to mark the end of the type annotation for a custom type control.

type RecordBuilder after afters before befores debouncingReceiverCollector deltaInitialiser errorCollector alertEmitter fns idleSetter initialCmds initialStates initialiser makeSetters parser subscriptionCollector toOutput updater viewer stateWrapper stateUnwrapper deltaWrapper deltaUnwrapper emptyDeltas

A data structure used to build records

toOutput
(a16 -> a16)
(a15 -> a15)
(a14 -> a14)
(a13 -> a13)
(a12 -> a12)
(a11 -> a11)
(b -> c -> c)
(a10 -> a10)
(d -> e -> e)
(f -> g -> g)
(a9 -> a9)
(a8 -> a8)
(a7 -> a7)
(a6 -> a6)
toOutput
(a5 -> a5)
(a4 -> a4)
(a3 -> a3)
(a2 -> a2)
(a1 -> a1)
(a -> a)

A combinator that produces a record type.

import Control exposing (Control, Record, Field, EndRecord, record, field, endRecord, string, int)


type alias MyRecord =
    { foo : String
    , bar : Int
    }


record
    (\foo bar ->
        { foo = foo
        , bar = bar
        }
    )
    |> field .foo string
    |> field .bar int
    |> endRecord


--: Control context (Record (Field String (Field String EndRecord))) (Record (Field String (Field String EndRecord))) MyRecord
(recordOutput9 -> output7)
-> AdvancedControl context9 input9 state9 delta11 output7
b
a22
(( Delta delta12, a21 ) -> c5)
(( ( Delta delta12, a21 ) -> c5, a20 ) -> c4)
( a19
-> { alerts : List Alert
, fns : restFns7
, states : restStates6
}
-> List Alert
)
( a18
-> { cmds : List (Cmd recordDelta2)
, deltaSetters : restDeltaSetters2
, fieldDeltas : restDeltas2
}
-> List (Cmd recordDelta2)
)
( a17
-> { alerts : List Alert
, feedback : List Feedback
, fns : restFns6
, states : restStates5
}
-> List Feedback
)
( a16
-> { alerts : List Alert
, context : context6
, fns : restFns5
, states : restStates4
}
-> List Alert
)
( Path
-> ( RecordFns context9 input9 state9 delta11 output7 recordOutput9
, a15
)
-> c3
)
( a14
-> { fns : restFns4
, inputStates : restInputStates
, outputStates : restInputStates -> outputStates
}
-> outputStates
)
(Path -> ( Cmd (Delta delta11), a13 ) -> c2)
(Path -> ( State state9, a12 ) -> c1)
( a11
-> { deltaSetters : deltaSetters
, deltas : List (Cmd msg1)
, fns : fns
, recordInput : recordOutput4
, states : nextState -> states
}
-> statesAndDeltas
)
( a10
-> { afters : d
, befores : e
, output : a9 -> c
, wrapDeltas : f -> g
}
-> h
)
( a8
-> { context : context3
, fns : restFns3
, states : restStates3
, toOutputResult : Result (List Feedback) output1_1
}
-> Result (List Feedback) output2_1
)
( a7
-> { context : context2
, fns : restFns2
, listSubs : List (Sub msg)
, setters : restDeltas1
, states : restStates2
}
-> List (Sub msg)
)
toOutput
( a6
-> { context : context1
, deltaSetters : restDeltaSetters1
, deltas : restDeltas
, fns : restFns1
, newCmds : List (Cmd recordDelta1)
, newStates : restStates1 -> intermediateRecordStates
, states : restStates1
}
-> { newCmds : List (Cmd recordDelta1)
, newStates : finalRecordStates
}
)
( a5
-> { alerts : List Alert
, context : context
, deltaSetters : restDeltaSetters
, fns : restFns
, states : restStates
, views : List (Subcontrol recordDelta)
}
-> List (Subcontrol recordDelta)
)
(a4 -> restStateTuples -> restStateFields)
(a3 -> restStateFields -> Maybe restStateTuples)
(a2 -> restDeltaTuples -> restDeltaFields)
(a1 -> restDeltaFields -> Maybe restDeltaTuples)
a
( Delta delta10, b )
( b, a22 )
(a21 -> c5)
(a20 -> c4)
( a19
-> { alerts : List Alert
, fns :
( RecordFns context8 input8 state8 delta9 output6 recordOutput8
, restFns7
)
, states : ( State state8, restStates6 )
}
-> List Alert
)
( a18
-> { cmds : List (Cmd recordDelta2)
, deltaSetters : ( fieldDelta1 -> recordDelta2, restDeltaSetters2 )
, fieldDeltas : ( Cmd fieldDelta1, restDeltas2 )
}
-> List (Cmd recordDelta2)
)
( a17
-> { alerts : List Alert
, feedback : List Feedback
, fns :
( RecordFns context7 input7 state7 delta8 output5 recordOutput7
, restFns6
)
, states : ( State state7, restStates5 )
}
-> List Feedback
)
( a16
-> { alerts : List Alert
, context : context6
, fns :
( RecordFns context6 input6 state6 delta7 output4 recordOutput6
, restFns5
)
, states : ( State state6, restStates4 )
}
-> List Alert
)
(Path -> a15 -> c3)
( a14
-> { fns :
context5
input5
inputState
delta6
output3
recordOutput5
, restFns4
)
, inputStates : ( State inputState, restInputStates )
, outputStates :
( State inputState, restInputStates ) -> outputStates
}
-> outputStates
)
(Path -> a13 -> c2)
(Path -> a12 -> c1)
( a11
-> { deltaSetters : ( Delta delta5 -> msg1, deltaSetters )
, deltas : List (Cmd msg1)
, fns :
( RecordFns context4 input4 state5 delta5 input4 recordOutput4
, fns
)
, recordInput : recordOutput4
, states : ( State state5, nextState ) -> states
}
-> statesAndDeltas
)
( a10
-> { k
| afters : ( i, d )
, befores : ( ( j, i ) -> f, e )
, output : ( j -> g, a9 ) -> c
, wrapDeltas : f -> g
}
-> h
)
( a8
-> { context : context3
, fns :
( RecordFns context3 input3 state4 delta4 output0 recordOutput3
, restFns3
)
, states : ( State state4, restStates3 )
, toOutputResult : Result (List Feedback) (output0 -> output1_1)
}
-> Result (List Feedback) output2_1
)
( a7
-> { context : context2
, fns :
( RecordFns context2 input2 state3 delta3 output2 recordOutput2
, restFns2
)
, listSubs : List (Sub msg)
, setters : ( Delta delta3 -> msg, restDeltas1 )
, states : ( State state3, restStates2 )
}
-> List (Sub msg)
)
toOutput
( a6
-> { context : context1
, deltaSetters :
( Delta fieldDelta -> recordDelta1, restDeltaSetters1 )
, deltas : ( Delta fieldDelta, restDeltas )
, fns :
context1
input1
state2
fieldDelta
output1
recordOutput1
, restFns1
)
, newCmds : List (Cmd recordDelta1)
, newStates :
( State state2, restStates1 ) -> intermediateRecordStates
, states : ( State state2, restStates1 )
}
-> { newCmds : List (Cmd recordDelta1)
, newStates : finalRecordStates
}
)
( a5
-> { alerts : List Alert
, context : context
, deltaSetters : ( Delta delta2 -> recordDelta, restDeltaSetters )
, fns :
( RecordFns context input state1 delta2 output recordOutput
, restFns
)
, states : ( State state1, restStates )
, views : List (Subcontrol recordDelta)
}
-> List (Subcontrol recordDelta)
)
( a4
-> ( State state, restStateTuples )
-> Field state restStateFields
)
( a3
-> Field state restStateFields
-> Maybe ( State state, restStateTuples )
)
( a2
-> ( Delta delta1, restDeltaTuples )
-> Field delta1 restDeltaFields
)
( a1
-> Field delta1 restDeltaFields
-> Maybe ( Delta delta1, restDeltaTuples )
)
( Delta delta, a )

Add a field to a record combinator.

import Control exposing (Control, Record, Field, EndRecord, record, field, endRecord, string)


type alias Hello =
    { hello : String }


record Hello
    |> field .hello string
    |> endRecord


--: Control context (Record (Field String EndRecord)) (Record (Field String EndRecord)) Hello
after
afters
before
(End -> b)
( ( { alerts : List Alert
, fns : End
, states : End
}
-> List Alert
)
-> { alerts : List Alert
, fns :
( RecordFns context input1 state delta output1 recordInput
, restFns
)
, states : ( State state, restStates )
}
-> List Alert
)
( ( { cmds : List (Cmd (Record (Field delta restDeltaFields)))
, deltaSetters : End
, fieldDeltas : End
}
-> List (Cmd (Record (Field delta restDeltaFields)))
)
-> { cmds : List (Cmd (Record (Field delta restDeltaFields)))
, deltaSetters :
( Delta delta -> Record (Field delta restDeltaFields)
, restDeltaSetters
)
, fieldDeltas : a1
}
-> List (Cmd (Record (Field delta restDeltaFields)))
)
( ( { alerts : List Alert
, feedback : List Feedback
, fns : End
, states : End
}
-> List Feedback
)
-> { alerts : List Alert
, feedback : List Feedback
, fns :
( RecordFns context input1 state delta output1 recordInput
, restFns
)
, states : ( State state, restStates )
}
-> List Feedback
)
( ( { alerts : List Alert
, context : context
, fns : End
, states : End
}
-> List Alert
)
-> { alerts : List Alert
, context : context
, fns :
( RecordFns context input1 state delta output1 recordInput
, restFns
)
, states : ( State state, restStates )
}
-> List Alert
)
( Path
-> End
-> ( RecordFns context input1 state delta output1 recordInput
, restFns
)
)
( ( { fns : End
, inputStates : End
, outputStates : End -> ( State state, restStates )
}
-> ( State state, restStates )
)
-> { fns :
( RecordFns context input1 state delta output1 recordInput
, restFns
)
, inputStates : ( State state, restStates )
, outputStates : identity1 -> identity1
}
-> ( State state, restStates )
)
(Path -> End -> a1)
(Path -> End -> ( State state, restStates ))
( ( { deltaSetters : End
, deltas : List (Cmd (Record (Field delta restDeltaFields)))
, fns : End
, recordInput : input
, states : End -> ( State state, restStates )
}
-> ( State ( State state, restStates )
, Cmd (Record (Field delta restDeltaFields))
)
)
-> { deltaSetters :
( Delta delta -> Record (Field delta restDeltaFields)
, restDeltaSetters
)
, deltas : List (Cmd (Record (Field delta restDeltaFields)))
, fns :
( RecordFns context input1 state delta output1 recordInput
, restFns
)
, recordInput : input
, states : identity -> identity
}
-> ( State ( State state, restStates )
, Cmd (Record (Field delta restDeltaFields))
)
)
( ({ f | output : End -> c } -> c)
-> { afters : afters
, befores : b
, output : a -> a
, wrapDeltas :
( Delta delta, restDeltas )
-> Record (Field delta restDeltaFields)
}
-> ( Delta delta -> Record (Field delta restDeltaFields)
, restDeltaSetters
)
)
( ( { context : context
, fns : End
, states : End
, toOutputResult : Result (List Feedback) output
}
-> Result (List Feedback) output
)
-> { context : context
, fns :
( RecordFns context input1 state delta output1 recordInput
, restFns
)
, states : ( State state, restStates )
, toOutputResult : Result (List Feedback) output1_1
}
-> Result (List Feedback) output
)
( ( { context : context
, fns : End
, listSubs : List (Sub (Record (Field delta restDeltaFields)))
, setters : End
, states : End
}
-> List (Sub (Record (Field delta restDeltaFields)))
)
-> { context : context
, fns :
( RecordFns context input1 state delta output1 recordInput
, restFns
)
, listSubs : List (Sub (Record (Field delta restDeltaFields)))
, setters :
( Delta delta -> Record (Field delta restDeltaFields)
, restDeltaSetters
)
, states : ( State state, restStates )
}
-> List (Sub (Record (Field delta restDeltaFields)))
)
output1_1
( ( { context : context
, deltaSetters : End
, deltas : End
, fns : End
, newCmds : List (Cmd (Record (Field delta restDeltaFields)))
, newStates : End -> ( State state, restStates )
, states : End
}
-> { newCmds : List (Cmd (Record (Field delta restDeltaFields)))
, newStates : End -> ( State state, restStates )
}
)
-> { context : context
, deltaSetters :
( Delta delta -> Record (Field delta restDeltaFields)
, restDeltaSetters
)
, deltas : ( Delta delta, restDeltas )
, fns :
( RecordFns context input1 state delta output1 recordInput
, restFns
)
, newCmds : List e
, newStates : d -> d
, states : ( State state, restStates )
}
-> { newCmds : List (Cmd (Record (Field delta restDeltaFields)))
, newStates : End -> ( State state, restStates )
}
)
( ( { alerts : List Alert
, context : context
, deltaSetters : End
, fns : End
, states : End
, views :
List (Subcontrol (Record (Field delta restDeltaFields)))
}
-> List (Subcontrol (Record (Field delta restDeltaFields)))
)
-> { alerts : List Alert
, context : context
, deltaSetters :
( Delta delta -> Record (Field delta restDeltaFields)
, restDeltaSetters
)
, fns :
( RecordFns context input1 state delta output1 recordInput
, restFns
)
, states : ( State state, restStates )
, views :
List (Subcontrol (Record (Field delta restDeltaFields)))
}
-> List (Subcontrol (Record (Field delta restDeltaFields)))
)
( (End -> EndRecord)
-> ( State state, restStates )
-> Field state restStateFields
)
( (EndRecord -> Maybe End)
-> Field state restStateFields
-> Maybe ( State state, restStates )
)
( (End -> EndRecord)
-> ( Delta delta, restDeltas )
-> Field delta restDeltaFields
)
( (EndRecord -> Maybe End)
-> Field delta restDeltaFields
-> Maybe ( Delta delta, restDeltas )
)
( Delta delta, restDeltas )
context
input
(Record (Field state restStateFields))
(Record (Field delta restDeltaFields))
output

Finalise the construction of a record combinator.

import Control exposing (Control, Record, Field, EndRecord, record, field, endRecord, string)


type alias Hello =
    { hello : String }


record Hello
    |> field .hello string
    |> endRecord


--: Control context (Record (Field String EndRecord)) (Record (Field String EndRecord)) Hello
type alias LayoutConfig delta =
{ selected : Int
, selectMsg : Int -> Delta delta
, id : String
, label : String
, class : String
}

Configuration for the layout function

type alias Subcontrol delta =
{ html : List (Html (Delta delta))
, label : String
, index : Int
}

Data used by the layout function to render a subcontrol of a combinator.

( LayoutConfig delta
-> List (Subcontrol delta)
-> List (Html (Delta delta))
)
-> Control context state delta output
-> Control context state delta output

Define the HTML output for a combinator.

This is useful if you want to alter the way that the combinator's subcontrols are laid out on the screen.

For example, you could use it to present a record control as a wizard, rather than a list. The wizard example below is very naive, but hopefully gives you the idea.

import Control exposing (Control, Record, Field, EndRecord, record, field, endRecord, layout, string, int)
import Html
import Html.Attributes
import Html.Events


type alias MyRecord =
    { foo : String
    , bar : Int
    }


record
    (\foo bar ->
        { foo = foo
        , bar = bar
        }
    )
    |> field .foo string
    |> field .bar int
    |> endRecord
    |> layout wizard


--: Control context (Record (Field String (Field String EndRecord))) (Record (Field String (Field String EndRecord))) MyRecord


wizard config subcontrols =
    let
        currentPage =
            config.selected


        totalPages =
            List.length subcontrols


        currentPageView =
            subcontrols
                |> List.filter (\{ index } -> index == currentPage)
                |> List.map .html
                |> List.concat


        nextClicked =
            config.selectMsg (currentPage + 1)


        backClicked =
            config.selectMsg (currentPage - 1)


        navigationButton txt msg =
            Html.button
                [ Html.Attributes.type_ "button"
                , Html.Events.onClick msg
                ]
                [ Html.text txt ]
    in
    [ Html.div
        [ Html.Attributes.id config.id
        , Html.Attributes.class config.class
        ]
        ([ Html.h1 [] [ Html.text "Wizard!" ]
         , navigationButton "Back" backClicked
         , Html.text
            (String.join " "
                [ "page"
                , String.fromInt currentPage
                , "of"
                , String.fromInt totalPages
                ]
            )
         , navigationButton "Next" nextClicked
         ]
            ++ currentPageView
        )
    ]

Building custom type combinators

type CustomType customType

A type used to contain the state or delta of a custom type control.

type Variant args restTags

A type used to contain the state or delta of a variant for a custom type control.

type Arg arg restArgs

A type that can contain the state or delta of an argument for a variant within a custom type control.

A type used to mark the end of the type annotation for a variant within a custom type control.

A type used to mark the end of the type annotation for a custom type control.

type CustomTypeBuilder applyInputs debouncingReceiverCollector deltaAfter deltaAfters deltaBefore deltaBefores destructor errorCollector alertEmitter fns idleSetter initialCmds initialStates initialiseDeltas makeDeltaSetters makeStateSetters parser stateAfter stateAfters stateBefore stateBefores stateInserter subscriptionCollector toArgStates updater viewer stateWrapper stateUnwrapper deltaWrapper deltaUnwrapper emptyDeltas

A data structure used to build custom types

destructor
(applyInputs -> applyInputs)
(debouncingReceiverCollector -> debouncingReceiverCollector)
(deltaBefore -> deltaBefore)
(deltaBefores -> deltaBefores)
destructor
(errorCollector -> errorCollector)
(alertEmitter -> alertEmitter)
(Path -> fns -> fns)
(idleSetter -> idleSetter)
(Path -> initialDeltas -> initialDeltas)
(Path -> initialStates -> initialStates)
(initialiseDeltas -> initialiseDeltas)
(makeDeltaSetters -> makeDeltaSetters)
(makeStateSetters -> makeStateSetters)
(parser -> parser)
(stateBefore -> stateBefore)
(stateBefores -> stateBefores)
(stateInserter -> stateInserter)
(subscriptionCollector -> subscriptionCollector)
(toArgStates -> toArgStates)
(updater -> updater)
(viewer -> viewer)
(stateWrapper -> stateWrapper)
(stateUnwrapper -> stateUnwrapper)
(deltaWrapper -> deltaWrapper)
(deltaUnwrapper -> deltaUnwrapper)

A combinator that produces a custom type.

import Control exposing (Control, CustomType, Variant, Arg, EndVariant, EndCustomType, customType, variant0, variant1, variant2, endCustomType, string, int, float)


type MyCustomType
    = NoArgs
    | OneArg String
    | TwoArgs Int Float




customType
    (\noArgs oneArg twoArgs variant ->
        case variant of
            NoArgs ->
                noArgs


            OneArg arg1 ->
                oneArg arg1


            TwoArgs arg1 arg2 ->
                twoArgs arg1 arg2
    )
    |> variant0 "NoArgs" NoArgs
    |> variant1 "OneArg" OneArg string
    |> variant2 "TwoArgs" TwoArgs int float
    |> endCustomType


--: Control context (Control.CustomType (Control.Variant Control.EndVariant (Control.Variant (Control.Arg String Control.EndVariant) (Control.Variant (Control.Arg String (Control.Arg String Control.EndVariant)) Control.EndCustomType)))) (Control.CustomType (Control.Variant Control.EndVariant (Control.Variant (Control.Arg String Control.EndVariant) (Control.Variant (Control.Arg String (Control.Arg String Control.EndVariant)) Control.EndCustomType)))) MyCustomType
String
-> output9
( a29
-> { destructor : destructor1
, inputToStateConverters : restInputToStateConverters
}
-> tag
-> ( State tagStates2, Cmd delta10 )
)
( a28
-> { fns : restFns6
, receivers : List Alert
, states : restStates5
}
-> List Alert
)
b2
a27
(( Delta delta11, a26 ) -> c8)
(( ( Delta delta11, a26 ) -> c8, a25 ) -> c7)
destructor
( a24
-> { alerts : List Alert
, feedback : List Feedback
, fns : restFns5
, states : restStates4
}
-> List Feedback
)
( a23
-> { alerts : List Alert
, context : context6
, fns : restFns4
, selectedTag : Int
, states : restStates3
}
-> List Alert
)
( Path
-> ( ControlFns context9 output9 EndVariant EndVariant output9
, a22
)
-> c6
)
( a21
-> { fns : restFns3
, initialStates : restInitialStates
, selectedTag : Int
, toFinalStates :
restInitialStates -> ( State finalState, restFinalStates )
}
-> ( State finalState, restFinalStates )
)
(Path -> ( Cmd (Delta EndVariant), a20 ) -> c5)
(Path -> ( State EndVariant, a19 ) -> c4)
( a18
-> { cmds : List (Cmd customTypeDelta2)
, deltaSetters : restDeltaSetters3
, deltas : restDeltas1
}
-> List (Cmd customTypeDelta2)
)
( a17
-> { afters : d
, befores : e
, output : a16 -> c3
, wrapDeltas : f -> g
}
-> h
)
( a15
-> { controlFns : restControlFns
, deltaSetters : restDeltaSetters2
, finalTagStates : b1 -> ( toFinalTagState, restToFinalTagStates )
, initialTagStates : ( State argState1, restArgStates1 )
, inputTuplizers : restInputTuplizers
, maybeOverridesAfter : restMaybeOverridesAfter
, maybeOverridesBefore : restMaybeOverridesBefore
, tagStateOverrider :
( { initialTagStates : End
, maybeOverrides : End
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : End -> tagStates1
}
-> State tagStates1
)
-> { initialTagStates : ( State argState1, restArgStates1 )
, maybeOverrides :
( Maybe (State argState1), restMaybeOverrides1 )
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : identity -> identity
}
-> State tagStates1
}
-> ( toFinalTagState, restToFinalTagStates )
)
( a14
-> { context : context3
, fns : restFns2
, parsedResult : Result (List Feedback) output3
, selectedTag : Int
, states : restStates2
}
-> Result (List Feedback) output3
)
b
a12
(( Maybe a30, a11 ) -> c2)
(( ( Maybe a30, a11 ) -> c2, a10 ) -> c1)
( a9
-> { initialTagStates : restArgStates
, maybeOverrides : restMaybeOverrides
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : restArgStates -> tagStates
}
-> State tagStates
)
( a8
-> { context : context2
, deltaSetters : restDeltaSetters1
, fns : restFns1
, states : restStates1
, subs : List (Sub customTypeDelta1)
}
-> List (Sub customTypeDelta1)
)
(( (output9 -> i) -> i, a7 ) -> c)
( a6
-> { context : context1
, deltaSetters : restDeltaSetters
, deltas : restDeltas
, fns : restFns
, newCmds : List (Cmd customTypeDelta)
, newStates : restStates -> intermediateCustomTypeStates
, states : restStates
}
-> { newCmds : List (Cmd customTypeDelta)
, newStates : End -> finalCustomTypeStates
}
)
( a5
-> { alerts : List Alert
, context : context
, deltaSetters : j
, fns : l
, states : m
, subcontrols :
List
{ html : List (Html msg)
, index : Int
, label : String
}
}
-> n
)
(a4 -> restStateTuples1 -> restStateTags1)
(a3 -> restStateTags -> Maybe restStateTuples)
(a2 -> restDeltaTuples1 -> restDeltaTags1)
(a1 -> restDeltaTags -> Maybe restDeltaTuples)
a
( a29
-> { destructor : inputToStateConverter -> destructor1
, inputToStateConverters :
( inputToStateConverter, restInputToStateConverters )
}
-> tag
-> ( State tagStates2, Cmd delta10 )
)
( a28
-> { fns :
( ControlFns context8 input8 state9 delta9 output8, restFns6 )
, receivers : List Alert
, states : ( State state9, restStates5 )
}
-> List Alert
)
( Delta delta8, b2 )
( b2, a27 )
(a26 -> c8)
(a25 -> c7)
destructor
( a24
-> { alerts : List Alert
, feedback : List Feedback
, fns :
( ControlFns context7 input7 state8 delta7 output7, restFns5 )
, states : ( State state8, restStates4 )
}
-> List Feedback
)
( a23
-> { alerts : List Alert
, context : context6
, fns :
( ControlFns context6 input6 state7 delta6 output6, restFns4 )
, selectedTag : Int
, states : ( State state7, restStates3 )
}
-> List Alert
)
(Path -> a22 -> c6)
( a21
-> { fns :
( ControlFns context5 input5 initialState delta5 output5
, restFns3
)
, initialStates : ( State initialState, restInitialStates )
, selectedTag : Int
, toFinalStates :
( State initialState, restInitialStates )
-> ( State finalState, restFinalStates )
}
-> ( State finalState, restFinalStates )
)
(Path -> a20 -> c5)
(Path -> a19 -> c4)
( a18
-> { cmds : List (Cmd customTypeDelta2)
, deltaSetters :
( tagDelta3 -> customTypeDelta2, restDeltaSetters3 )
, deltas : ( Cmd tagDelta3, restDeltas1 )
}
-> List (Cmd customTypeDelta2)
)
( a17
-> { q
| afters : ( o, d )
, befores : ( ( p, o ) -> f, e )
, output : ( p -> g, a16 ) -> c3
, wrapDeltas : f -> g
}
-> h
)
( a15
-> { controlFns :
( ControlFns context4 input4 state6 argDelta output4
, restControlFns
)
, deltaSetters : ( Delta argDelta -> tagDelta2, restDeltaSetters2 )
, finalTagStates :
( k, b1 ) -> ( toFinalTagState, restToFinalTagStates )
, initialTagStates : ( State argState1, restArgStates1 )
, inputTuplizers :
( (input4 -> ( State tagStates1, Cmd (Delta tagDelta2) )) -> k
, restInputTuplizers
)
, maybeOverridesAfter :
( maybeOverrideAfter, restMaybeOverridesAfter )
, maybeOverridesBefore :
( ( Maybe (State state6), maybeOverrideAfter )
-> ( Maybe (State argState1), restMaybeOverrides1 )
, restMaybeOverridesBefore
)
, tagStateOverrider :
( { initialTagStates : End
, maybeOverrides : End
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : End -> tagStates1
}
-> State tagStates1
)
-> { initialTagStates : ( State argState1, restArgStates1 )
, maybeOverrides :
( Maybe (State argState1), restMaybeOverrides1 )
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : identity -> identity
}
-> State tagStates1
}
-> ( toFinalTagState, restToFinalTagStates )
)
( a14
-> { context : context3
, fns :
( ControlFns context3 input3 state5 delta4 output3, restFns2 )
, parsedResult : Result (List Feedback) output3
, selectedTag : Int
, states : ( State state5, restStates2 )
}
-> Result (List Feedback) output3
)
( Maybe a13, b )
( b, a12 )
(a11 -> c2)
(a10 -> c1)
( a9
-> { initialTagStates : ( State argState, restArgStates )
, maybeOverrides : ( Maybe (State argState), restMaybeOverrides )
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : ( State argState, restArgStates ) -> tagStates
}
-> State tagStates
)
( a8
-> { context : context2
, deltaSetters :
( Delta tagDelta1 -> customTypeDelta1, restDeltaSetters1 )
, fns :
( ControlFns context2 input2 state4 tagDelta1 output2
, restFns1
)
, states : ( State state4, restStates1 )
, subs : List (Sub customTypeDelta1)
}
-> List (Sub customTypeDelta1)
)
(a7 -> c)
( a6
-> { context : context1
, deltaSetters :
( Delta tagDelta -> customTypeDelta, restDeltaSetters )
, deltas : ( Delta tagDelta, restDeltas )
, fns :
( ControlFns context1 input1 state3 tagDelta output1, restFns )
, newCmds : List (Cmd customTypeDelta)
, newStates :
( State state3, restStates ) -> intermediateCustomTypeStates
, states : ( State state3, restStates )
}
-> { newCmds : List (Cmd customTypeDelta)
, newStates : End -> finalCustomTypeStates
}
)
( a5
-> { r
| alerts : List Alert
, context : context
, deltaSetters : ( Delta delta3 -> msg, j )
, fns : ( ControlFns context input state2 delta3 output, l )
, states : ( State state2, m )
, subcontrols :
List
{ html : List (Html msg)
, index : Int
, label : String
}
}
-> n
)
( a4
-> ( State state1, restStateTuples1 )
-> Variant state1 restStateTags1
)
( a3
-> Variant state restStateTags
-> Maybe ( State state, restStateTuples )
)
( a2
-> ( Delta delta2, restDeltaTuples1 )
-> Variant delta2 restDeltaTags1
)
( a1
-> Variant delta1 restDeltaTags
-> Maybe ( Delta delta1, restDeltaTuples )
)
( Delta delta, a )

Add a variant with no arguments to a custom type.

import Control exposing (Control, CustomType, Variant, Arg, EndVariant, EndCustomType, customType, endCustomType, variant0)


type Unit
    = Unit




customType
    (\unit variant ->
        case variant of
            Unit ->
                unit
    )
    |> variant0 "Unit" Unit
    |> endCustomType


--: Control context (CustomType (Variant EndVariant EndCustomType)) (CustomType (Variant EndVariant EndCustomType)) Unit
String
-> (output10 -> output9)
-> AdvancedControl context9 output10 state10 delta11 output10
( a29
-> { destructor : destructor1
, inputToStateConverters : restInputToStateConverters
}
-> tag
-> ( State tagStates2, Cmd delta10 )
)
( a28
-> { fns : restFns6
, receivers : List Alert
, states : restStates5
}
-> List Alert
)
b2
a27
(( Delta delta12, a26 ) -> c8)
(( ( Delta delta12, a26 ) -> c8, a25 ) -> c7)
destructor
( a24
-> { alerts : List Alert
, feedback : List Feedback
, fns : restFns5
, states : restStates4
}
-> List Feedback
)
( a23
-> { alerts : List Alert
, context : context6
, fns : restFns4
, selectedTag : Int
, states : restStates3
}
-> List Alert
)
( Path
context9
( output10, b3 )
(Arg state10 EndVariant)
(Arg delta11 EndVariant)
output9
, a22
)
-> c6
)
( a21
-> { fns : restFns3
, initialStates : restInitialStates
, selectedTag : Int
, toFinalStates :
restInitialStates -> ( State finalState, restFinalStates )
}
-> ( State finalState, restFinalStates )
)
(Path -> ( Cmd (Delta (Arg delta11 EndVariant)), a20 ) -> c5)
(Path -> ( State (Arg state10 EndVariant), a19 ) -> c4)
( a18
-> { cmds : List (Cmd customTypeDelta2)
, deltaSetters : restDeltaSetters3
, deltas : restDeltas1
}
-> List (Cmd customTypeDelta2)
)
( a17
-> { afters : d
, befores : e
, output : a16 -> c3
, wrapDeltas : f -> g
}
-> h
)
( a15
-> { controlFns : restControlFns
, deltaSetters : restDeltaSetters2
, finalTagStates : b1 -> ( toFinalTagState, restToFinalTagStates )
, initialTagStates : ( State argState1, restArgStates1 )
, inputTuplizers : restInputTuplizers
, maybeOverridesAfter : restMaybeOverridesAfter
, maybeOverridesBefore : restMaybeOverridesBefore
, tagStateOverrider :
( { initialTagStates : End
, maybeOverrides : End
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : End -> tagStates1
}
-> State tagStates1
)
-> { initialTagStates : ( State argState1, restArgStates1 )
, maybeOverrides :
( Maybe (State argState1), restMaybeOverrides1 )
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : identity -> identity
}
-> State tagStates1
}
-> ( toFinalTagState, restToFinalTagStates )
)
( a14
-> { context : context3
, fns : restFns2
, parsedResult : Result (List Feedback) output3
, selectedTag : Int
, states : restStates2
}
-> Result (List Feedback) output3
)
b
a12
(( Maybe a30, a11 ) -> c2)
(( ( Maybe a30, a11 ) -> c2, a10 ) -> c1)
( a9
-> { initialTagStates : restArgStates
, maybeOverrides : restMaybeOverrides
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : restArgStates -> tagStates
}
-> State tagStates
)
( a8
-> { context : context2
, deltaSetters : restDeltaSetters1
, fns : restFns1
, states : restStates1
, subs : List (Sub customTypeDelta1)
}
-> List (Sub customTypeDelta1)
)
(( (( i, End ) -> j) -> i -> j, a7 ) -> c)
( a6
-> { context : context1
, deltaSetters : restDeltaSetters
, deltas : restDeltas
, fns : restFns
, newCmds : List (Cmd customTypeDelta)
, newStates : restStates -> intermediateCustomTypeStates
, states : restStates
}
-> { newCmds : List (Cmd customTypeDelta)
, newStates : End -> finalCustomTypeStates
}
)
( a5
-> { alerts : List Alert
, context : context
, deltaSetters : l
, fns : m
, states : n
, subcontrols :
List
{ html : List (Html msg)
, index : Int
, label : String
}
}
-> o
)
(a4 -> restStateTuples1 -> restStateTags1)
(a3 -> restStateTags -> Maybe restStateTuples)
(a2 -> restDeltaTuples1 -> restDeltaTags1)
(a1 -> restDeltaTags -> Maybe restDeltaTuples)
a
( a29
-> { destructor : inputToStateConverter -> destructor1
, inputToStateConverters :
( inputToStateConverter, restInputToStateConverters )
}
-> tag
-> ( State tagStates2, Cmd delta10 )
)
( a28
-> { fns :
( ControlFns context8 input8 state9 delta9 output8, restFns6 )
, receivers : List Alert
, states : ( State state9, restStates5 )
}
-> List Alert
)
( Delta delta8, b2 )
( b2, a27 )
(a26 -> c8)
(a25 -> c7)
destructor
( a24
-> { alerts : List Alert
, feedback : List Feedback
, fns :
( ControlFns context7 input7 state8 delta7 output7, restFns5 )
, states : ( State state8, restStates4 )
}
-> List Feedback
)
( a23
-> { alerts : List Alert
, context : context6
, fns :
( ControlFns context6 input6 state7 delta6 output6, restFns4 )
, selectedTag : Int
, states : ( State state7, restStates3 )
}
-> List Alert
)
(Path -> a22 -> c6)
( a21
-> { fns :
( ControlFns context5 input5 initialState delta5 output5
, restFns3
)
, initialStates : ( State initialState, restInitialStates )
, selectedTag : Int
, toFinalStates :
( State initialState, restInitialStates )
-> ( State finalState, restFinalStates )
}
-> ( State finalState, restFinalStates )
)
(Path -> a20 -> c5)
(Path -> a19 -> c4)
( a18
-> { cmds : List (Cmd customTypeDelta2)
, deltaSetters :
( tagDelta3 -> customTypeDelta2, restDeltaSetters3 )
, deltas : ( Cmd tagDelta3, restDeltas1 )
}
-> List (Cmd customTypeDelta2)
)
( a17
-> { r
| afters : ( p, d )
, befores : ( ( q, p ) -> f, e )
, output : ( q -> g, a16 ) -> c3
, wrapDeltas : f -> g
}
-> h
)
( a15
-> { controlFns :
( ControlFns context4 input4 state6 argDelta output4
, restControlFns
)
, deltaSetters : ( Delta argDelta -> tagDelta2, restDeltaSetters2 )
, finalTagStates :
( k, b1 ) -> ( toFinalTagState, restToFinalTagStates )
, initialTagStates : ( State argState1, restArgStates1 )
, inputTuplizers :
( (input4 -> ( State tagStates1, Cmd (Delta tagDelta2) )) -> k
, restInputTuplizers
)
, maybeOverridesAfter :
( maybeOverrideAfter, restMaybeOverridesAfter )
, maybeOverridesBefore :
( ( Maybe (State state6), maybeOverrideAfter )
-> ( Maybe (State argState1), restMaybeOverrides1 )
, restMaybeOverridesBefore
)
, tagStateOverrider :
( { initialTagStates : End
, maybeOverrides : End
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : End -> tagStates1
}
-> State tagStates1
)
-> { initialTagStates : ( State argState1, restArgStates1 )
, maybeOverrides :
( Maybe (State argState1), restMaybeOverrides1 )
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : identity -> identity
}
-> State tagStates1
}
-> ( toFinalTagState, restToFinalTagStates )
)
( a14
-> { context : context3
, fns :
( ControlFns context3 input3 state5 delta4 output3, restFns2 )
, parsedResult : Result (List Feedback) output3
, selectedTag : Int
, states : ( State state5, restStates2 )
}
-> Result (List Feedback) output3
)
( Maybe a13, b )
( b, a12 )
(a11 -> c2)
(a10 -> c1)
( a9
-> { initialTagStates : ( State argState, restArgStates )
, maybeOverrides : ( Maybe (State argState), restMaybeOverrides )
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : ( State argState, restArgStates ) -> tagStates
}
-> State tagStates
)
( a8
-> { context : context2
, deltaSetters :
( Delta tagDelta1 -> customTypeDelta1, restDeltaSetters1 )
, fns :
( ControlFns context2 input2 state4 tagDelta1 output2
, restFns1
)
, states : ( State state4, restStates1 )
, subs : List (Sub customTypeDelta1)
}
-> List (Sub customTypeDelta1)
)
(a7 -> c)
( a6
-> { context : context1
, deltaSetters :
( Delta tagDelta -> customTypeDelta, restDeltaSetters )
, deltas : ( Delta tagDelta, restDeltas )
, fns :
( ControlFns context1 input1 state3 tagDelta output1, restFns )
, newCmds : List (Cmd customTypeDelta)
, newStates :
( State state3, restStates ) -> intermediateCustomTypeStates
, states : ( State state3, restStates )
}
-> { newCmds : List (Cmd customTypeDelta)
, newStates : End -> finalCustomTypeStates
}
)
( a5
-> { s
| alerts : List Alert
, context : context
, deltaSetters : ( Delta delta3 -> msg, l )
, fns : ( ControlFns context input state2 delta3 output, m )
, states : ( State state2, n )
, subcontrols :
List
{ html : List (Html msg)
, index : Int
, label : String
}
}
-> o
)
( a4
-> ( State state1, restStateTuples1 )
-> Variant state1 restStateTags1
)
( a3
-> Variant state restStateTags
-> Maybe ( State state, restStateTuples )
)
( a2
-> ( Delta delta2, restDeltaTuples1 )
-> Variant delta2 restDeltaTags1
)
( a1
-> Variant delta1 restDeltaTags
-> Maybe ( Delta delta1, restDeltaTuples )
)
( Delta delta, a )

Add a variant with one argument to a custom type.

import Control exposing (Control, CustomType, Variant, Arg, EndVariant, EndCustomType, customType, endCustomType, variant1, int, string)


type alias MyResult =
    Result String Int


customType
    (\ok err variant ->
        case variant of
            Ok value ->
                ok value


            Err error ->
                err error
    )
    |> variant1 "Ok" Ok int
    |> variant1 "Err" Err string
    |> endCustomType


--: Control context (CustomType (Variant (Arg String EndVariant) (Variant (Arg String EndVariant) EndCustomType))) (CustomType (Variant (Arg String EndVariant) (Variant (Arg String EndVariant) EndCustomType))) MyResult
String
-> (output11 -> output10 -> output9)
-> AdvancedControl context9 output11 state11 delta12 output11
-> AdvancedControl context9 output10 state10 delta11 output10
( a29
-> { destructor : destructor1
, inputToStateConverters : restInputToStateConverters
}
-> tag
-> ( State tagStates2, Cmd delta10 )
)
( a28
-> { fns : restFns6
, receivers : List Alert
, states : restStates5
}
-> List Alert
)
b2
a27
(( Delta delta13, a26 ) -> c8)
(( ( Delta delta13, a26 ) -> c8, a25 ) -> c7)
destructor
( a24
-> { alerts : List Alert
, feedback : List Feedback
, fns : restFns5
, states : restStates4
}
-> List Feedback
)
( a23
-> { alerts : List Alert
, context : context6
, fns : restFns4
, selectedTag : Int
, states : restStates3
}
-> List Alert
)
( Path
context9
( output11, ( output10, b3 ) )
(Arg state11 (Arg state10 EndVariant))
(Arg delta12 (Arg delta11 EndVariant))
output9
, a22
)
-> c6
)
( a21
-> { fns : restFns3
, initialStates : restInitialStates
, selectedTag : Int
, toFinalStates :
restInitialStates -> ( State finalState, restFinalStates )
}
-> ( State finalState, restFinalStates )
)
( Path
-> ( Cmd (Delta (Arg delta12 (Arg delta11 EndVariant))), a20 )
-> c5
)
( Path
-> ( State (Arg state11 (Arg state10 EndVariant)), a19 )
-> c4
)
( a18
-> { cmds : List (Cmd customTypeDelta2)
, deltaSetters : restDeltaSetters3
, deltas : restDeltas1
}
-> List (Cmd customTypeDelta2)
)
( a17
-> { afters : d
, befores : e
, output : a16 -> c3
, wrapDeltas : f -> g
}
-> h
)
( a15
-> { controlFns : restControlFns
, deltaSetters : restDeltaSetters2
, finalTagStates : b1 -> ( toFinalTagState, restToFinalTagStates )
, initialTagStates : ( State argState1, restArgStates1 )
, inputTuplizers : restInputTuplizers
, maybeOverridesAfter : restMaybeOverridesAfter
, maybeOverridesBefore : restMaybeOverridesBefore
, tagStateOverrider :
( { initialTagStates : End
, maybeOverrides : End
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : End -> tagStates1
}
-> State tagStates1
)
-> { initialTagStates : ( State argState1, restArgStates1 )
, maybeOverrides :
( Maybe (State argState1), restMaybeOverrides1 )
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : identity -> identity
}
-> State tagStates1
}
-> ( toFinalTagState, restToFinalTagStates )
)
( a14
-> { context : context3
, fns : restFns2
, parsedResult : Result (List Feedback) output3
, selectedTag : Int
, states : restStates2
}
-> Result (List Feedback) output3
)
b
a12
(( Maybe a30, a11 ) -> c2)
(( ( Maybe a30, a11 ) -> c2, a10 ) -> c1)
( a9
-> { initialTagStates : restArgStates
, maybeOverrides : restMaybeOverrides
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : restArgStates -> tagStates
}
-> State tagStates
)
( a8
-> { context : context2
, deltaSetters : restDeltaSetters1
, fns : restFns1
, states : restStates1
, subs : List (Sub customTypeDelta1)
}
-> List (Sub customTypeDelta1)
)
(( (( i, ( j, End ) ) -> l) -> i -> j -> l, a7 ) -> c)
( a6
-> { context : context1
, deltaSetters : restDeltaSetters
, deltas : restDeltas
, fns : restFns
, newCmds : List (Cmd customTypeDelta)
, newStates : restStates -> intermediateCustomTypeStates
, states : restStates
}
-> { newCmds : List (Cmd customTypeDelta)
, newStates : End -> finalCustomTypeStates
}
)
( a5
-> { alerts : List Alert
, context : context
, deltaSetters : m
, fns : n
, states : o
, subcontrols :
List
{ html : List (Html msg)
, index : Int
, label : String
}
}
-> p
)
(a4 -> restStateTuples1 -> restStateTags1)
(a3 -> restStateTags -> Maybe restStateTuples)
(a2 -> restDeltaTuples1 -> restDeltaTags1)
(a1 -> restDeltaTags -> Maybe restDeltaTuples)
a
( a29
-> { destructor : inputToStateConverter -> destructor1
, inputToStateConverters :
( inputToStateConverter, restInputToStateConverters )
}
-> tag
-> ( State tagStates2, Cmd delta10 )
)
( a28
-> { fns :
( ControlFns context8 input8 state9 delta9 output8, restFns6 )
, receivers : List Alert
, states : ( State state9, restStates5 )
}
-> List Alert
)
( Delta delta8, b2 )
( b2, a27 )
(a26 -> c8)
(a25 -> c7)
destructor
( a24
-> { alerts : List Alert
, feedback : List Feedback
, fns :
( ControlFns context7 input7 state8 delta7 output7, restFns5 )
, states : ( State state8, restStates4 )
}
-> List Feedback
)
( a23
-> { alerts : List Alert
, context : context6
, fns :
( ControlFns context6 input6 state7 delta6 output6, restFns4 )
, selectedTag : Int
, states : ( State state7, restStates3 )
}
-> List Alert
)
(Path -> a22 -> c6)
( a21
-> { fns :
( ControlFns context5 input5 initialState delta5 output5
, restFns3
)
, initialStates : ( State initialState, restInitialStates )
, selectedTag : Int
, toFinalStates :
( State initialState, restInitialStates )
-> ( State finalState, restFinalStates )
}
-> ( State finalState, restFinalStates )
)
(Path -> a20 -> c5)
(Path -> a19 -> c4)
( a18
-> { cmds : List (Cmd customTypeDelta2)
, deltaSetters :
( tagDelta3 -> customTypeDelta2, restDeltaSetters3 )
, deltas : ( Cmd tagDelta3, restDeltas1 )
}
-> List (Cmd customTypeDelta2)
)
( a17
-> { s
| afters : ( q, d )
, befores : ( ( r, q ) -> f, e )
, output : ( r -> g, a16 ) -> c3
, wrapDeltas : f -> g
}
-> h
)
( a15
-> { controlFns :
( ControlFns context4 input4 state6 argDelta output4
, restControlFns
)
, deltaSetters : ( Delta argDelta -> tagDelta2, restDeltaSetters2 )
, finalTagStates :
( k, b1 ) -> ( toFinalTagState, restToFinalTagStates )
, initialTagStates : ( State argState1, restArgStates1 )
, inputTuplizers :
( (input4 -> ( State tagStates1, Cmd (Delta tagDelta2) )) -> k
, restInputTuplizers
)
, maybeOverridesAfter :
( maybeOverrideAfter, restMaybeOverridesAfter )
, maybeOverridesBefore :
( ( Maybe (State state6), maybeOverrideAfter )
-> ( Maybe (State argState1), restMaybeOverrides1 )
, restMaybeOverridesBefore
)
, tagStateOverrider :
( { initialTagStates : End
, maybeOverrides : End
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : End -> tagStates1
}
-> State tagStates1
)
-> { initialTagStates : ( State argState1, restArgStates1 )
, maybeOverrides :
( Maybe (State argState1), restMaybeOverrides1 )
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : identity -> identity
}
-> State tagStates1
}
-> ( toFinalTagState, restToFinalTagStates )
)
( a14
-> { context : context3
, fns :
( ControlFns context3 input3 state5 delta4 output3, restFns2 )
, parsedResult : Result (List Feedback) output3
, selectedTag : Int
, states : ( State state5, restStates2 )
}
-> Result (List Feedback) output3
)
( Maybe a13, b )
( b, a12 )
(a11 -> c2)
(a10 -> c1)
( a9
-> { initialTagStates : ( State argState, restArgStates )
, maybeOverrides : ( Maybe (State argState), restMaybeOverrides )
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : ( State argState, restArgStates ) -> tagStates
}
-> State tagStates
)
( a8
-> { context : context2
, deltaSetters :
( Delta tagDelta1 -> customTypeDelta1, restDeltaSetters1 )
, fns :
( ControlFns context2 input2 state4 tagDelta1 output2
, restFns1
)
, states : ( State state4, restStates1 )
, subs : List (Sub customTypeDelta1)
}
-> List (Sub customTypeDelta1)
)
(a7 -> c)
( a6
-> { context : context1
, deltaSetters :
( Delta tagDelta -> customTypeDelta, restDeltaSetters )
, deltas : ( Delta tagDelta, restDeltas )
, fns :
( ControlFns context1 input1 state3 tagDelta output1, restFns )
, newCmds : List (Cmd customTypeDelta)
, newStates :
( State state3, restStates ) -> intermediateCustomTypeStates
, states : ( State state3, restStates )
}
-> { newCmds : List (Cmd customTypeDelta)
, newStates : End -> finalCustomTypeStates
}
)
( a5
-> { t
| alerts : List Alert
, context : context
, deltaSetters : ( Delta delta3 -> msg, m )
, fns : ( ControlFns context input state2 delta3 output, n )
, states : ( State state2, o )
, subcontrols :
List
{ html : List (Html msg)
, index : Int
, label : String
}
}
-> p
)
( a4
-> ( State state1, restStateTuples1 )
-> Variant state1 restStateTags1
)
( a3
-> Variant state restStateTags
-> Maybe ( State state, restStateTuples )
)
( a2
-> ( Delta delta2, restDeltaTuples1 )
-> Variant delta2 restDeltaTags1
)
( a1
-> Variant delta1 restDeltaTags
-> Maybe ( Delta delta1, restDeltaTuples )
)
( Delta delta, a )

Add a variant with two arguments to a custom type.

import Control exposing (Control, CustomType, Variant, Arg, EndVariant, EndCustomType, customType, variant2, endCustomType, float)


type Point
    = Point Float Float


customType
    (\point variant ->
        case variant of
            Point x y ->
                point x y
    )
    |> variant2 "Point" Point float float
    |> endCustomType


--: Control context (CustomType (Variant (Arg String (Arg String EndVariant)) EndCustomType)) (CustomType (Variant (Arg String (Arg String EndVariant)) EndCustomType)) Point
String
-> (output12 -> output11 -> output10 -> output9)
-> AdvancedControl context9 output12 state12 delta13 output12
-> AdvancedControl context9 output11 state11 delta12 output11
-> AdvancedControl context9 output10 state10 delta11 output10
( a29
-> { destructor : destructor1
, inputToStateConverters : restInputToStateConverters
}
-> tag
-> ( State tagStates2, Cmd delta10 )
)
( a28
-> { fns : restFns6
, receivers : List Alert
, states : restStates5
}
-> List Alert
)
b2
a27
(( Delta delta14, a26 ) -> c8)
(( ( Delta delta14, a26 ) -> c8, a25 ) -> c7)
destructor
( a24
-> { alerts : List Alert
, feedback : List Feedback
, fns : restFns5
, states : restStates4
}
-> List Feedback
)
( a23
-> { alerts : List Alert
, context : context6
, fns : restFns4
, selectedTag : Int
, states : restStates3
}
-> List Alert
)
( Path
context9
( output12, ( output11, ( output10, b3 ) ) )
(Arg state12 (Arg state11 (Arg state10 EndVariant)))
(Arg delta13 (Arg delta12 (Arg delta11 EndVariant)))
output9
, a22
)
-> c6
)
( a21
-> { fns : restFns3
, initialStates : restInitialStates
, selectedTag : Int
, toFinalStates :
restInitialStates -> ( State finalState, restFinalStates )
}
-> ( State finalState, restFinalStates )
)
( Path
-> ( Cmd
(Delta (Arg delta13 (Arg delta12 (Arg delta11 EndVariant))))
, a20
)
-> c5
)
( Path
-> ( State (Arg state12 (Arg state11 (Arg state10 EndVariant)))
, a19
)
-> c4
)
( a18
-> { cmds : List (Cmd customTypeDelta2)
, deltaSetters : restDeltaSetters3
, deltas : restDeltas1
}
-> List (Cmd customTypeDelta2)
)
( a17
-> { afters : d
, befores : e
, output : a16 -> c3
, wrapDeltas : f -> g
}
-> h
)
( a15
-> { controlFns : restControlFns
, deltaSetters : restDeltaSetters2
, finalTagStates : b1 -> ( toFinalTagState, restToFinalTagStates )
, initialTagStates : ( State argState1, restArgStates1 )
, inputTuplizers : restInputTuplizers
, maybeOverridesAfter : restMaybeOverridesAfter
, maybeOverridesBefore : restMaybeOverridesBefore
, tagStateOverrider :
( { initialTagStates : End
, maybeOverrides : End
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : End -> tagStates1
}
-> State tagStates1
)
-> { initialTagStates : ( State argState1, restArgStates1 )
, maybeOverrides :
( Maybe (State argState1), restMaybeOverrides1 )
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : identity -> identity
}
-> State tagStates1
}
-> ( toFinalTagState, restToFinalTagStates )
)
( a14
-> { context : context3
, fns : restFns2
, parsedResult : Result (List Feedback) output3
, selectedTag : Int
, states : restStates2
}
-> Result (List Feedback) output3
)
b
a12
(( Maybe a30, a11 ) -> c2)
(( ( Maybe a30, a11 ) -> c2, a10 ) -> c1)
( a9
-> { initialTagStates : restArgStates
, maybeOverrides : restMaybeOverrides
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : restArgStates -> tagStates
}
-> State tagStates
)
( a8
-> { context : context2
, deltaSetters : restDeltaSetters1
, fns : restFns1
, states : restStates1
, subs : List (Sub customTypeDelta1)
}
-> List (Sub customTypeDelta1)
)
(( (( i, ( j, ( l, End ) ) ) -> m) -> i -> j -> l -> m, a7 ) -> c)
( a6
-> { context : context1
, deltaSetters : restDeltaSetters
, deltas : restDeltas
, fns : restFns
, newCmds : List (Cmd customTypeDelta)
, newStates : restStates -> intermediateCustomTypeStates
, states : restStates
}
-> { newCmds : List (Cmd customTypeDelta)
, newStates : End -> finalCustomTypeStates
}
)
( a5
-> { alerts : List Alert
, context : context
, deltaSetters : n
, fns : o
, states : p
, subcontrols :
List
{ html : List (Html msg)
, index : Int
, label : String
}
}
-> q
)
(a4 -> restStateTuples1 -> restStateTags1)
(a3 -> restStateTags -> Maybe restStateTuples)
(a2 -> restDeltaTuples1 -> restDeltaTags1)
(a1 -> restDeltaTags -> Maybe restDeltaTuples)
a
( a29
-> { destructor : inputToStateConverter -> destructor1
, inputToStateConverters :
( inputToStateConverter, restInputToStateConverters )
}
-> tag
-> ( State tagStates2, Cmd delta10 )
)
( a28
-> { fns :
( ControlFns context8 input8 state9 delta9 output8, restFns6 )
, receivers : List Alert
, states : ( State state9, restStates5 )
}
-> List Alert
)
( Delta delta8, b2 )
( b2, a27 )
(a26 -> c8)
(a25 -> c7)
destructor
( a24
-> { alerts : List Alert
, feedback : List Feedback
, fns :
( ControlFns context7 input7 state8 delta7 output7, restFns5 )
, states : ( State state8, restStates4 )
}
-> List Feedback
)
( a23
-> { alerts : List Alert
, context : context6
, fns :
( ControlFns context6 input6 state7 delta6 output6, restFns4 )
, selectedTag : Int
, states : ( State state7, restStates3 )
}
-> List Alert
)
(Path -> a22 -> c6)
( a21
-> { fns :
( ControlFns context5 input5 initialState delta5 output5
, restFns3
)
, initialStates : ( State initialState, restInitialStates )
, selectedTag : Int
, toFinalStates :
( State initialState, restInitialStates )
-> ( State finalState, restFinalStates )
}
-> ( State finalState, restFinalStates )
)
(Path -> a20 -> c5)
(Path -> a19 -> c4)
( a18
-> { cmds : List (Cmd customTypeDelta2)
, deltaSetters :
( tagDelta3 -> customTypeDelta2, restDeltaSetters3 )
, deltas : ( Cmd tagDelta3, restDeltas1 )
}
-> List (Cmd customTypeDelta2)
)
( a17
-> { t
| afters : ( r, d )
, befores : ( ( s, r ) -> f, e )
, output : ( s -> g, a16 ) -> c3
, wrapDeltas : f -> g
}
-> h
)
( a15
-> { controlFns :
( ControlFns context4 input4 state6 argDelta output4
, restControlFns
)
, deltaSetters : ( Delta argDelta -> tagDelta2, restDeltaSetters2 )
, finalTagStates :
( k, b1 ) -> ( toFinalTagState, restToFinalTagStates )
, initialTagStates : ( State argState1, restArgStates1 )
, inputTuplizers :
( (input4 -> ( State tagStates1, Cmd (Delta tagDelta2) )) -> k
, restInputTuplizers
)
, maybeOverridesAfter :
( maybeOverrideAfter, restMaybeOverridesAfter )
, maybeOverridesBefore :
( ( Maybe (State state6), maybeOverrideAfter )
-> ( Maybe (State argState1), restMaybeOverrides1 )
, restMaybeOverridesBefore
)
, tagStateOverrider :
( { initialTagStates : End
, maybeOverrides : End
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : End -> tagStates1
}
-> State tagStates1
)
-> { initialTagStates : ( State argState1, restArgStates1 )
, maybeOverrides :
( Maybe (State argState1), restMaybeOverrides1 )
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : identity -> identity
}
-> State tagStates1
}
-> ( toFinalTagState, restToFinalTagStates )
)
( a14
-> { context : context3
, fns :
( ControlFns context3 input3 state5 delta4 output3, restFns2 )
, parsedResult : Result (List Feedback) output3
, selectedTag : Int
, states : ( State state5, restStates2 )
}
-> Result (List Feedback) output3
)
( Maybe a13, b )
( b, a12 )
(a11 -> c2)
(a10 -> c1)
( a9
-> { initialTagStates : ( State argState, restArgStates )
, maybeOverrides : ( Maybe (State argState), restMaybeOverrides )
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : ( State argState, restArgStates ) -> tagStates
}
-> State tagStates
)
( a8
-> { context : context2
, deltaSetters :
( Delta tagDelta1 -> customTypeDelta1, restDeltaSetters1 )
, fns :
( ControlFns context2 input2 state4 tagDelta1 output2
, restFns1
)
, states : ( State state4, restStates1 )
, subs : List (Sub customTypeDelta1)
}
-> List (Sub customTypeDelta1)
)
(a7 -> c)
( a6
-> { context : context1
, deltaSetters :
( Delta tagDelta -> customTypeDelta, restDeltaSetters )
, deltas : ( Delta tagDelta, restDeltas )
, fns :
( ControlFns context1 input1 state3 tagDelta output1, restFns )
, newCmds : List (Cmd customTypeDelta)
, newStates :
( State state3, restStates ) -> intermediateCustomTypeStates
, states : ( State state3, restStates )
}
-> { newCmds : List (Cmd customTypeDelta)
, newStates : End -> finalCustomTypeStates
}
)
( a5
-> { u
| alerts : List Alert
, context : context
, deltaSetters : ( Delta delta3 -> msg, n )
, fns : ( ControlFns context input state2 delta3 output, o )
, states : ( State state2, p )
, subcontrols :
List
{ html : List (Html msg)
, index : Int
, label : String
}
}
-> q
)
( a4
-> ( State state1, restStateTuples1 )
-> Variant state1 restStateTags1
)
( a3
-> Variant state restStateTags
-> Maybe ( State state, restStateTuples )
)
( a2
-> ( Delta delta2, restDeltaTuples1 )
-> Variant delta2 restDeltaTags1
)
( a1
-> Variant delta1 restDeltaTags
-> Maybe ( Delta delta1, restDeltaTuples )
)
( Delta delta, a )

Add a variant with three arguments to a custom type.

import Control exposing (Control, CustomType, Variant, Arg, EndVariant, EndCustomType, customType, variant3, endCustomType, float)


type Point3D
    = Point3D Float Float Float


customType
    (\point3D variant ->
        case variant of
            Point3D x y z ->
                point3D x y z
    )
    |> variant3 "Point3D" Point3D float float float
    |> endCustomType


--: Control context (CustomType (Variant (Arg String (Arg String (Arg String EndVariant))) EndCustomType)) (CustomType (Variant (Arg String (Arg String (Arg String EndVariant))) EndCustomType)) Point3D
String
-> (output13 -> output12 -> output11 -> output10 -> output9)
-> AdvancedControl context9 output13 state13 delta14 output13
-> AdvancedControl context9 output12 state12 delta13 output12
-> AdvancedControl context9 output11 state11 delta12 output11
-> AdvancedControl context9 output10 state10 delta11 output10
( a29
-> { destructor : destructor1
, inputToStateConverters : restInputToStateConverters
}
-> tag
-> ( State tagStates2, Cmd delta10 )
)
( a28
-> { fns : restFns6
, receivers : List Alert
, states : restStates5
}
-> List Alert
)
b2
a27
(( Delta delta15, a26 ) -> c8)
(( ( Delta delta15, a26 ) -> c8, a25 ) -> c7)
destructor
( a24
-> { alerts : List Alert
, feedback : List Feedback
, fns : restFns5
, states : restStates4
}
-> List Feedback
)
( a23
-> { alerts : List Alert
, context : context6
, fns : restFns4
, selectedTag : Int
, states : restStates3
}
-> List Alert
)
( Path
context9
( output13, ( output12, ( output11, ( output10, b3 ) ) ) )
(Arg
state13
(Arg state12 (Arg state11 (Arg state10 EndVariant)))
)
(Arg
delta14
(Arg delta13 (Arg delta12 (Arg delta11 EndVariant)))
)
output9
, a22
)
-> c6
)
( a21
-> { fns : restFns3
, initialStates : restInitialStates
, selectedTag : Int
, toFinalStates :
restInitialStates -> ( State finalState, restFinalStates )
}
-> ( State finalState, restFinalStates )
)
( Path
-> ( Cmd
(Arg
delta14
(Arg delta13 (Arg delta12 (Arg delta11 EndVariant)))
)
)
, a20
)
-> c5
)
( Path
-> ( State
(Arg
state13
(Arg state12 (Arg state11 (Arg state10 EndVariant)))
)
, a19
)
-> c4
)
( a18
-> { cmds : List (Cmd customTypeDelta2)
, deltaSetters : restDeltaSetters3
, deltas : restDeltas1
}
-> List (Cmd customTypeDelta2)
)
( a17
-> { afters : d
, befores : e
, output : a16 -> c3
, wrapDeltas : f -> g
}
-> h
)
( a15
-> { controlFns : restControlFns
, deltaSetters : restDeltaSetters2
, finalTagStates : b1 -> ( toFinalTagState, restToFinalTagStates )
, initialTagStates : ( State argState1, restArgStates1 )
, inputTuplizers : restInputTuplizers
, maybeOverridesAfter : restMaybeOverridesAfter
, maybeOverridesBefore : restMaybeOverridesBefore
, tagStateOverrider :
( { initialTagStates : End
, maybeOverrides : End
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : End -> tagStates1
}
-> State tagStates1
)
-> { initialTagStates : ( State argState1, restArgStates1 )
, maybeOverrides :
( Maybe (State argState1), restMaybeOverrides1 )
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : identity -> identity
}
-> State tagStates1
}
-> ( toFinalTagState, restToFinalTagStates )
)
( a14
-> { context : context3
, fns : restFns2
, parsedResult : Result (List Feedback) output3
, selectedTag : Int
, states : restStates2
}
-> Result (List Feedback) output3
)
b
a12
(( Maybe a30, a11 ) -> c2)
(( ( Maybe a30, a11 ) -> c2, a10 ) -> c1)
( a9
-> { initialTagStates : restArgStates
, maybeOverrides : restMaybeOverrides
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : restArgStates -> tagStates
}
-> State tagStates
)
( a8
-> { context : context2
, deltaSetters : restDeltaSetters1
, fns : restFns1
, states : restStates1
, subs : List (Sub customTypeDelta1)
}
-> List (Sub customTypeDelta1)
)
( ( (( i, ( j, ( l, ( m, End ) ) ) ) -> n) -> i -> j -> l -> m -> n, a7 )
-> c
)
( a6
-> { context : context1
, deltaSetters : restDeltaSetters
, deltas : restDeltas
, fns : restFns
, newCmds : List (Cmd customTypeDelta)
, newStates : restStates -> intermediateCustomTypeStates
, states : restStates
}
-> { newCmds : List (Cmd customTypeDelta)
, newStates : End -> finalCustomTypeStates
}
)
( a5
-> { alerts : List Alert
, context : context
, deltaSetters : o
, fns : p
, states : q
, subcontrols :
List
{ html : List (Html msg)
, index : Int
, label : String
}
}
-> r
)
(a4 -> restStateTuples1 -> restStateTags1)
(a3 -> restStateTags -> Maybe restStateTuples)
(a2 -> restDeltaTuples1 -> restDeltaTags1)
(a1 -> restDeltaTags -> Maybe restDeltaTuples)
a
( a29
-> { destructor : inputToStateConverter -> destructor1
, inputToStateConverters :
( inputToStateConverter, restInputToStateConverters )
}
-> tag
-> ( State tagStates2, Cmd delta10 )
)
( a28
-> { fns :
( ControlFns context8 input8 state9 delta9 output8, restFns6 )
, receivers : List Alert
, states : ( State state9, restStates5 )
}
-> List Alert
)
( Delta delta8, b2 )
( b2, a27 )
(a26 -> c8)
(a25 -> c7)
destructor
( a24
-> { alerts : List Alert
, feedback : List Feedback
, fns :
( ControlFns context7 input7 state8 delta7 output7, restFns5 )
, states : ( State state8, restStates4 )
}
-> List Feedback
)
( a23
-> { alerts : List Alert
, context : context6
, fns :
( ControlFns context6 input6 state7 delta6 output6, restFns4 )
, selectedTag : Int
, states : ( State state7, restStates3 )
}
-> List Alert
)
(Path -> a22 -> c6)
( a21
-> { fns :
( ControlFns context5 input5 initialState delta5 output5
, restFns3
)
, initialStates : ( State initialState, restInitialStates )
, selectedTag : Int
, toFinalStates :
( State initialState, restInitialStates )
-> ( State finalState, restFinalStates )
}
-> ( State finalState, restFinalStates )
)
(Path -> a20 -> c5)
(Path -> a19 -> c4)
( a18
-> { cmds : List (Cmd customTypeDelta2)
, deltaSetters :
( tagDelta3 -> customTypeDelta2, restDeltaSetters3 )
, deltas : ( Cmd tagDelta3, restDeltas1 )
}
-> List (Cmd customTypeDelta2)
)
( a17
-> { u
| afters : ( s, d )
, befores : ( ( t, s ) -> f, e )
, output : ( t -> g, a16 ) -> c3
, wrapDeltas : f -> g
}
-> h
)
( a15
-> { controlFns :
( ControlFns context4 input4 state6 argDelta output4
, restControlFns
)
, deltaSetters : ( Delta argDelta -> tagDelta2, restDeltaSetters2 )
, finalTagStates :
( k, b1 ) -> ( toFinalTagState, restToFinalTagStates )
, initialTagStates : ( State argState1, restArgStates1 )
, inputTuplizers :
( (input4 -> ( State tagStates1, Cmd (Delta tagDelta2) )) -> k
, restInputTuplizers
)
, maybeOverridesAfter :
( maybeOverrideAfter, restMaybeOverridesAfter )
, maybeOverridesBefore :
( ( Maybe (State state6), maybeOverrideAfter )
-> ( Maybe (State argState1), restMaybeOverrides1 )
, restMaybeOverridesBefore
)
, tagStateOverrider :
( { initialTagStates : End
, maybeOverrides : End
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : End -> tagStates1
}
-> State tagStates1
)
-> { initialTagStates : ( State argState1, restArgStates1 )
, maybeOverrides :
( Maybe (State argState1), restMaybeOverrides1 )
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : identity -> identity
}
-> State tagStates1
}
-> ( toFinalTagState, restToFinalTagStates )
)
( a14
-> { context : context3
, fns :
( ControlFns context3 input3 state5 delta4 output3, restFns2 )
, parsedResult : Result (List Feedback) output3
, selectedTag : Int
, states : ( State state5, restStates2 )
}
-> Result (List Feedback) output3
)
( Maybe a13, b )
( b, a12 )
(a11 -> c2)
(a10 -> c1)
( a9
-> { initialTagStates : ( State argState, restArgStates )
, maybeOverrides : ( Maybe (State argState), restMaybeOverrides )
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : ( State argState, restArgStates ) -> tagStates
}
-> State tagStates
)
( a8
-> { context : context2
, deltaSetters :
( Delta tagDelta1 -> customTypeDelta1, restDeltaSetters1 )
, fns :
( ControlFns context2 input2 state4 tagDelta1 output2
, restFns1
)
, states : ( State state4, restStates1 )
, subs : List (Sub customTypeDelta1)
}
-> List (Sub customTypeDelta1)
)
(a7 -> c)
( a6
-> { context : context1
, deltaSetters :
( Delta tagDelta -> customTypeDelta, restDeltaSetters )
, deltas : ( Delta tagDelta, restDeltas )
, fns :
( ControlFns context1 input1 state3 tagDelta output1, restFns )
, newCmds : List (Cmd customTypeDelta)
, newStates :
( State state3, restStates ) -> intermediateCustomTypeStates
, states : ( State state3, restStates )
}
-> { newCmds : List (Cmd customTypeDelta)
, newStates : End -> finalCustomTypeStates
}
)
( a5
-> { v
| alerts : List Alert
, context : context
, deltaSetters : ( Delta delta3 -> msg, o )
, fns : ( ControlFns context input state2 delta3 output, p )
, states : ( State state2, q )
, subcontrols :
List
{ html : List (Html msg)
, index : Int
, label : String
}
}
-> r
)
( a4
-> ( State state1, restStateTuples1 )
-> Variant state1 restStateTags1
)
( a3
-> Variant state restStateTags
-> Maybe ( State state, restStateTuples )
)
( a2
-> ( Delta delta2, restDeltaTuples1 )
-> Variant delta2 restDeltaTags1
)
( a1
-> Variant delta1 restDeltaTags
-> Maybe ( Delta delta1, restDeltaTuples )
)
( Delta delta, a )

Add a variant with four arguments to a custom type.

String
-> (output14 -> output13 -> output12 -> output11 -> output10 -> output9)
-> AdvancedControl context9 output14 state14 delta15 output14
-> AdvancedControl context9 output13 state13 delta14 output13
-> AdvancedControl context9 output12 state12 delta13 output12
-> AdvancedControl context9 output11 state11 delta12 output11
-> AdvancedControl context9 output10 state10 delta11 output10
( a29
-> { destructor : destructor1
, inputToStateConverters : restInputToStateConverters
}
-> tag
-> ( State tagStates2, Cmd delta10 )
)
( a28
-> { fns : restFns6
, receivers : List Alert
, states : restStates5
}
-> List Alert
)
b2
a27
(( Delta delta16, a26 ) -> c8)
(( ( Delta delta16, a26 ) -> c8, a25 ) -> c7)
destructor
( a24
-> { alerts : List Alert
, feedback : List Feedback
, fns : restFns5
, states : restStates4
}
-> List Feedback
)
( a23
-> { alerts : List Alert
, context : context6
, fns : restFns4
, selectedTag : Int
, states : restStates3
}
-> List Alert
)
( Path
context9
( output14
, ( output13, ( output12, ( output11, ( output10, b3 ) ) ) )
)
(Arg
state14
(Arg
state13
(Arg state12 (Arg state11 (Arg state10 EndVariant)))
)
)
(Arg
delta15
(Arg
delta14
(Arg delta13 (Arg delta12 (Arg delta11 EndVariant)))
)
)
output9
, a22
)
-> c6
)
( a21
-> { fns : restFns3
, initialStates : restInitialStates
, selectedTag : Int
, toFinalStates :
restInitialStates -> ( State finalState, restFinalStates )
}
-> ( State finalState, restFinalStates )
)
( Path
-> ( Cmd
(Arg
delta15
(Arg
delta14
(Arg delta13 (Arg delta12 (Arg delta11 EndVariant)))
)
)
)
, a20
)
-> c5
)
( Path
-> ( State
(Arg
state14
(Arg
state13
(Arg state12 (Arg state11 (Arg state10 EndVariant)))
)
)
, a19
)
-> c4
)
( a18
-> { cmds : List (Cmd customTypeDelta2)
, deltaSetters : restDeltaSetters3
, deltas : restDeltas1
}
-> List (Cmd customTypeDelta2)
)
( a17
-> { afters : d
, befores : e
, output : a16 -> c3
, wrapDeltas : f -> g
}
-> h
)
( a15
-> { controlFns : restControlFns
, deltaSetters : restDeltaSetters2
, finalTagStates : b1 -> ( toFinalTagState, restToFinalTagStates )
, initialTagStates : ( State argState1, restArgStates1 )
, inputTuplizers : restInputTuplizers
, maybeOverridesAfter : restMaybeOverridesAfter
, maybeOverridesBefore : restMaybeOverridesBefore
, tagStateOverrider :
( { initialTagStates : End
, maybeOverrides : End
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : End -> tagStates1
}
-> State tagStates1
)
-> { initialTagStates : ( State argState1, restArgStates1 )
, maybeOverrides :
( Maybe (State argState1), restMaybeOverrides1 )
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : identity -> identity
}
-> State tagStates1
}
-> ( toFinalTagState, restToFinalTagStates )
)
( a14
-> { context : context3
, fns : restFns2
, parsedResult : Result (List Feedback) output3
, selectedTag : Int
, states : restStates2
}
-> Result (List Feedback) output3
)
b
a12
(( Maybe a30, a11 ) -> c2)
(( ( Maybe a30, a11 ) -> c2, a10 ) -> c1)
( a9
-> { initialTagStates : restArgStates
, maybeOverrides : restMaybeOverrides
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : restArgStates -> tagStates
}
-> State tagStates
)
( a8
-> { context : context2
, deltaSetters : restDeltaSetters1
, fns : restFns1
, states : restStates1
, subs : List (Sub customTypeDelta1)
}
-> List (Sub customTypeDelta1)
)
( ( (( i, ( j, ( l, ( m, ( n, End ) ) ) ) ) -> o) -> i -> j -> l -> m -> n -> o
, a7
)
-> c
)
( a6
-> { context : context1
, deltaSetters : restDeltaSetters
, deltas : restDeltas
, fns : restFns
, newCmds : List (Cmd customTypeDelta)
, newStates : restStates -> intermediateCustomTypeStates
, states : restStates
}
-> { newCmds : List (Cmd customTypeDelta)
, newStates : End -> finalCustomTypeStates
}
)
( a5
-> { alerts : List Alert
, context : context
, deltaSetters : p
, fns : q
, states : r
, subcontrols :
List
{ html : List (Html msg)
, index : Int
, label : String
}
}
-> s
)
(a4 -> restStateTuples1 -> restStateTags1)
(a3 -> restStateTags -> Maybe restStateTuples)
(a2 -> restDeltaTuples1 -> restDeltaTags1)
(a1 -> restDeltaTags -> Maybe restDeltaTuples)
a
( a29
-> { destructor : inputToStateConverter -> destructor1
, inputToStateConverters :
( inputToStateConverter, restInputToStateConverters )
}
-> tag
-> ( State tagStates2, Cmd delta10 )
)
( a28
-> { fns :
( ControlFns context8 input8 state9 delta9 output8, restFns6 )
, receivers : List Alert
, states : ( State state9, restStates5 )
}
-> List Alert
)
( Delta delta8, b2 )
( b2, a27 )
(a26 -> c8)
(a25 -> c7)
destructor
( a24
-> { alerts : List Alert
, feedback : List Feedback
, fns :
( ControlFns context7 input7 state8 delta7 output7, restFns5 )
, states : ( State state8, restStates4 )
}
-> List Feedback
)
( a23
-> { alerts : List Alert
, context : context6
, fns :
( ControlFns context6 input6 state7 delta6 output6, restFns4 )
, selectedTag : Int
, states : ( State state7, restStates3 )
}
-> List Alert
)
(Path -> a22 -> c6)
( a21
-> { fns :
( ControlFns context5 input5 initialState delta5 output5
, restFns3
)
, initialStates : ( State initialState, restInitialStates )
, selectedTag : Int
, toFinalStates :
( State initialState, restInitialStates )
-> ( State finalState, restFinalStates )
}
-> ( State finalState, restFinalStates )
)
(Path -> a20 -> c5)
(Path -> a19 -> c4)
( a18
-> { cmds : List (Cmd customTypeDelta2)
, deltaSetters :
( tagDelta3 -> customTypeDelta2, restDeltaSetters3 )
, deltas : ( Cmd tagDelta3, restDeltas1 )
}
-> List (Cmd customTypeDelta2)
)
( a17
-> { v
| afters : ( t, d )
, befores : ( ( u, t ) -> f, e )
, output : ( u -> g, a16 ) -> c3
, wrapDeltas : f -> g
}
-> h
)
( a15
-> { controlFns :
( ControlFns context4 input4 state6 argDelta output4
, restControlFns
)
, deltaSetters : ( Delta argDelta -> tagDelta2, restDeltaSetters2 )
, finalTagStates :
( k, b1 ) -> ( toFinalTagState, restToFinalTagStates )
, initialTagStates : ( State argState1, restArgStates1 )
, inputTuplizers :
( (input4 -> ( State tagStates1, Cmd (Delta tagDelta2) )) -> k
, restInputTuplizers
)
, maybeOverridesAfter :
( maybeOverrideAfter, restMaybeOverridesAfter )
, maybeOverridesBefore :
( ( Maybe (State state6), maybeOverrideAfter )
-> ( Maybe (State argState1), restMaybeOverrides1 )
, restMaybeOverridesBefore
)
, tagStateOverrider :
( { initialTagStates : End
, maybeOverrides : End
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : End -> tagStates1
}
-> State tagStates1
)
-> { initialTagStates : ( State argState1, restArgStates1 )
, maybeOverrides :
( Maybe (State argState1), restMaybeOverrides1 )
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : identity -> identity
}
-> State tagStates1
}
-> ( toFinalTagState, restToFinalTagStates )
)
( a14
-> { context : context3
, fns :
( ControlFns context3 input3 state5 delta4 output3, restFns2 )
, parsedResult : Result (List Feedback) output3
, selectedTag : Int
, states : ( State state5, restStates2 )
}
-> Result (List Feedback) output3
)
( Maybe a13, b )
( b, a12 )
(a11 -> c2)
(a10 -> c1)
( a9
-> { initialTagStates : ( State argState, restArgStates )
, maybeOverrides : ( Maybe (State argState), restMaybeOverrides )
, selectedTagIndex : Int
, thisTagIndex : Int
, toTagStates : ( State argState, restArgStates ) -> tagStates
}
-> State tagStates
)
( a8
-> { context : context2
, deltaSetters :
( Delta tagDelta1 -> customTypeDelta1, restDeltaSetters1 )
, fns :
( ControlFns context2 input2 state4 tagDelta1 output2
, restFns1
)
, states : ( State state4, restStates1 )
, subs : List (Sub customTypeDelta1)
}
-> List (Sub customTypeDelta1)
)
(a7 -> c)
( a6
-> { context : context1
, deltaSetters :
( Delta tagDelta -> customTypeDelta, restDeltaSetters )
, deltas : ( Delta tagDelta, restDeltas )
, fns :
( ControlFns context1 input1 state3 tagDelta output1, restFns )
, newCmds : List (Cmd customTypeDelta)
, newStates :
( State state3, restStates ) -> intermediateCustomTypeStates
, states : ( State state3, restStates )
}
-> { newCmds : List (Cmd customTypeDelta)
, newStates : End -> finalCustomTypeStates
}
)
( a5
-> { w
| alerts : List Alert
, context : context
, deltaSetters : ( Delta delta3 -> msg, p )
, fns : ( ControlFns context input state2 delta3 output, q )
, states : ( State state2, r )
, subcontrols :
List
{ html : List (Html msg)
, index : Int
, label : String
}
}
-> s
)
( a4
-> ( State state1, restStateTuples1 )
-> Variant state1 restStateTags1
)
( a3
-> Variant state restStateTags
-> Maybe ( State state, restStateTuples )
)
( a2
-> ( Delta delta2, restDeltaTuples1 )
-> Variant delta2 restDeltaTags1
)
( a1
-> Variant delta1 restDeltaTags
-> Maybe ( Delta delta1, restDeltaTuples )
)
( Delta delta, a )

Add a variant with five arguments to a custom type.

( ( { destructor :
input
-> ( State ( State state, restStates )
, Cmd (Delta (CustomType (Variant tagDelta restDeltaTags)))
)
, inputToStateConverters : End
}
-> input
-> ( State ( State state, restStates )
, Cmd (Delta (CustomType (Variant tagDelta restDeltaTags)))
)
)
-> { destructor : inputToStateConverter -> destructor
, inputToStateConverters :
( inputToStateConverter, restInputToStateConverters )
}
-> input
-> ( State ( State state, restStates )
, Cmd (Delta (CustomType (Variant tagDelta restDeltaTags)))
)
)
( ( { fns : End
, receivers : List Alert
, states : End
}
-> List Alert
)
-> { fns :
( ControlFns context input1 state tagDelta output, restFns )
, receivers : List Alert
, states : ( State state, restStates )
}
-> List Alert
)
deltaAfter
deltaAfters
deltaBefore
(End -> b)
(inputToStateConverter -> destructor)
( ( { alerts : List Alert
, feedback : List Feedback
, fns : End
, states : End
}
-> List Feedback
)
-> { alerts : List Alert
, feedback : List Feedback
, fns :
( ControlFns context input1 state tagDelta output, restFns )
, states : ( State state, restStates )
}
-> List Feedback
)
( ( { alerts : List Alert
, context : context
, fns : End
, selectedTag : Int
, states : End
}
-> List Alert
)
-> { alerts : List Alert
, context : context
, fns :
( ControlFns context input1 state tagDelta output, restFns )
, selectedTag : Int
, states : ( State state, restStates )
}
-> List Alert
)
( Path
-> End
-> ( ControlFns context input1 state tagDelta output, restFns )
)
( ( { fns : End
, initialStates : End
, selectedTag : Int
, toFinalStates : End -> ( State state, restStates )
}
-> ( State state, restStates )
)
-> { fns :
( ControlFns context input1 state tagDelta output, restFns )
, initialStates : ( State state, restStates )
, selectedTag : Int
, toFinalStates : identity2 -> identity2
}
-> ( State state, restStates )
)
(Path -> End -> ( Cmd (Delta tagDelta), restDeltas1 ))
(Path -> End -> ( State state, restStates ))
( ( { cmds : List (Cmd (CustomType (Variant tagDelta restDeltaTags)))
, deltaSetters : End
, deltas : End
}
-> List (Cmd (CustomType (Variant tagDelta restDeltaTags)))
)
-> { cmds : List (Cmd (CustomType (Variant tagDelta restDeltaTags)))
, deltaSetters :
( Delta tagDelta -> CustomType (Variant tagDelta restDeltaTags)
, restDeltaSetters
)
, deltas : ( Cmd (Delta tagDelta), restDeltas1 )
}
-> List (Cmd (CustomType (Variant tagDelta restDeltaTags)))
)
( ({ e | output : End -> d } -> d)
-> { afters : deltaAfters
, befores : b
, output : a -> a
, wrapDeltas :
( Delta delta, restDeltaTuples )
-> CustomType (Variant delta restDeltaTags1)
}
-> ( Delta tagDelta -> CustomType (Variant tagDelta restDeltaTags)
, restDeltaSetters
)
)
( ( { c
| finalTagStates :
End -> ( inputToStateConverter, restInputToStateConverters )
}
-> ( inputToStateConverter, restInputToStateConverters )
)
-> { controlFns :
( ControlFns context input1 state tagDelta output, restFns )
, deltaSetters :
( Delta tagDelta -> CustomType (Variant tagDelta restDeltaTags)
, restDeltaSetters
)
, finalTagStates : identity1 -> identity1
, initialTagStates : ( State state, restStates )
, inputTuplizers : inputTuplizers
, maybeOverridesAfter : maybeOverridesAfter
, maybeOverridesBefore : maybeOverridesBefore
, tagStateOverrider : tagStateOverrider
}
-> ( inputToStateConverter, restInputToStateConverters )
)
( ( { context : context
, fns : End
, parsedResult : Result (List Feedback) output
, selectedTag : Int
, states : End
}
-> Result (List Feedback) output
)
-> { context : context
, fns :
( ControlFns context input1 state tagDelta output, restFns )
, parsedResult : Result (List Feedback) output
, selectedTag : Int
, states : ( State state, restStates )
}
-> Result (List Feedback) output
)
stateAfter
maybeOverridesAfter
stateBefore
(End -> maybeOverridesBefore)
tagStateOverrider
( ( { context : context
, deltaSetters : End
, fns : End
, states : End
, subs : List (Sub (CustomType (Variant tagDelta restDeltaTags)))
}
-> List (Sub (CustomType (Variant tagDelta restDeltaTags)))
)
-> { context : context
, deltaSetters :
( Delta tagDelta -> CustomType (Variant tagDelta restDeltaTags)
, restDeltaSetters
)
, fns :
( ControlFns context input1 state tagDelta output, restFns )
, states : ( State state, restStates )
, subs : List (Sub (CustomType (Variant tagDelta restDeltaTags)))
}
-> List (Sub (CustomType (Variant tagDelta restDeltaTags)))
)
(End -> inputTuplizers)
( ( { context : context
, deltaSetters : End
, deltas : End
, fns : End
, newCmds :
List (Cmd (CustomType (Variant tagDelta restDeltaTags)))
, newStates : End -> ( State state, restStates )
, states : End
}
-> { newCmds :
List (Cmd (CustomType (Variant tagDelta restDeltaTags)))
, newStates : End -> ( State state, restStates )
}
)
-> { context : context
, deltaSetters :
( Delta tagDelta -> CustomType (Variant tagDelta restDeltaTags)
, restDeltaSetters
)
, deltas : ( Delta tagDelta, restDeltas )
, fns :
( ControlFns context input1 state tagDelta output, restFns )
, newCmds :
List (Cmd (CustomType (Variant tagDelta restDeltaTags)))
, newStates : identity -> identity
, states : ( State state, restStates )
}
-> { newCmds :
List (Cmd (CustomType (Variant tagDelta restDeltaTags)))
, newStates : End -> ( State state, restStates )
}
)
( ({ g | subcontrols : f } -> f)
-> { alerts : List Alert
, context : context
, deltaSetters :
( Delta tagDelta -> CustomType (Variant tagDelta restDeltaTags)
, restDeltaSetters
)
, fns :
( ControlFns context input1 state tagDelta output, restFns )
, states : ( State state, restStates )
, subcontrols : List h
}
-> List
{ i
| html :
List (Html (CustomType (Variant tagDelta restDeltaTags)))
, index : Int
, label : String
}
)
-> ( State state, restStates )
-> Variant state restStateTags
)
( (EndCustomType -> Maybe End)
-> Variant state restStateTags
-> Maybe ( State state, restStates )
)
-> ( Delta delta, restDeltaTuples )
-> Variant delta restDeltaTags1
)
( (EndCustomType -> Maybe End)
-> Variant tagDelta restDeltaTags
-> Maybe ( Delta tagDelta, restDeltas )
)
( Delta tagDelta, restDeltas )
context
input
(CustomType (Variant state restStateTags))
(CustomType (Variant tagDelta restDeltaTags))
output

Finalise the construction of a customType combinator.

type alias Foo
    = Bar
    | Baz


helloControl =
    customType
        (\bar baz variant ->
            case variant of
                Bar -> bar
                Baz -> baz
        )
        |> variant0 "Bar" Bar
        |> variant0 "Baz" Baz
        |> endCustomType

Creating a new control

type alias Definition state delta output =
{ blank : ( state, Cmd delta )
, prefill : output -> ( state, Cmd delta )
, update : delta -> state -> ( state, Cmd delta )
, view :
{ label : String
, id : String
, name : String
, class : String
, state : state
}
-> List (Html delta)
, subscriptions : state -> Sub delta
, parse : state -> Result (List String) output
, label : String
}

Definition for a custom control. You need to supply the following fields:

  • blank: an initial "empty" value for the state of the control, plus an initial (optional) Cmd to run. For a control whose state is of type String, the state value might be "", for a number it might be 0.
  • prefill: a function that uses a value of the control's output type to initialise the control's state and (optionally) run an initial Cmd. For a control that outputs an Int, but whose state is a String, you might use String.fromInt to convert the output value into a state value.
  • update: a function that updates the state of the control based on its existing state and a delta type that it receives. This is exactly analogous to an Elm program's update function, where state = Model and delta = Msg.
  • view: a function that outputs Html delta based on the state of the control. This is similar to an Elm program's view function, except instead of just taking the state as an argument, it takes a record containing the state plus the id, name, class, and label for the input.
  • subscriptions: a function that outputs Sub delta based on the state of the control. This is exactly analogous to an Elm program's subscriptions function.
  • parse: a function that attempts to parse the control's state into its output type, producing a result. If parsing fails, it can provide a list of errors.
  • label: a default label for the control.
Definition state delta output
-> Control context state delta output

Define a new type of Control, with any arbitrary state and delta types, and producing any arbitrary output type.

Here's how we could define a control like the famous counter example from the Elm Guide

import Control exposing (Control, define)
import Html
import Html.Attributes
import Html.Events


type CounterDelta
    = Increment
    | Decrement


define
    { blank = ( 0, Cmd.none )
    , prefill = \n -> ( n, Cmd.none )
    , update =
        \delta state ->
            case delta of
                Increment ->
                    ( state + 1, Cmd.none )


                Decrement ->
                    ( state - 1, Cmd.none )
    , view =
        \{ state, name, id, label, class } ->
            [ Html.div [ Html.Attributes.class class ]
                [ Html.label
                    [ Html.Attributes.for id ]
                    [ Html.text label ]
                , Html.div
                    [ Html.Attributes.id id
                    , Html.Attributes.name name
                    ]
                    [ Html.button
                        [ Html.Attributes.type_ "button"
                        , Html.Events.onClick Increment
                        ]
                        [ Html.text "+" ]
                    , Html.div [] [ Html.text <| String.fromInt state ]
                    , Html.button
                        [ Html.Attributes.type_ "button"
                        , Html.Events.onClick Decrement
                        ]
                        [ Html.text "-" ]
                    ]
                ]
            ]
    , subscriptions = \state -> Sub.none
    , parse = Ok
    , label = "Counter"
    }


--: Control context Int CounterDelta Int

Validating controls

Simple validation

(output -> Bool)
-> String
-> Control context state delta output
-> Control context state delta output

Display an error on a Control if the supplied predicate function returns True.

This causes the Control to fail validation.

import Control exposing (Control, failIf, int)


int
    |> failIf
        (\x -> x < 1)
        "This must be greater than zero!"


--: Control context String String Int
(output -> Bool)
-> String
-> Control context state delta output
-> Control context state delta output

Display a notification on a Control if the supplied predicate function returns True.

This just shows the user a message - it doesn't cause the Control to fail validation.

import Control exposing (Control, int, noteIf)


int
    |> noteIf
        (\x -> x < 1)
        "Maybe this should be greater than zero?"


--: Control context String String Int

Multi-field validation

You may want display errors on one or more controls based on the validation of another control higher up the tree.

Take the example of a password creation form. You want to validate that the "Enter password" and "Confirm password" fields both contain the same string, and show a helpful error message on the "Confirm password" field if they don't. You can achieve this as follows:

import Control exposing (Control, Record, Field, EndRecord, record, field, endRecord, string, alertIf, respond)


record
    (\password confirmation ->
        { password = password
        , confirmation = confirmation
        }
    )
    |> field .password string
    |> field .confirmation
        (string
            |> respond
                { alert = "passwords-do-not-match"
                , fail = True
                , message = "Passwords must match"
                , class = "control-feedback-fail"
                }
        )
    |> endRecord
    |> alertIf
        (\{ password, confirmation } ->
            password /= confirmation
        )
        "passwords-do-not-match"


--: Control context (Record (Field String (Field String EndRecord))) (Record (Field String (Field String EndRecord))) { password : String, confirmation : String }
(output -> Bool)
-> String
-> Control context state delta output
-> Control context state delta output

Emit an alert from a Control if the supplied predicate function returns True.

This is most useful with control combinators such as tuple or record, because it enables you to perform validations that involve multiple fields within the tuple or record.

import Control exposing (Control, Tuple, alertIf, tuple, int, string)


tuple int string
    |> alertIf (\( x, y ) -> x > 0) "int-greater-than-zero"


--: Control context (Tuple String String) (Tuple String String) ( Int, String )

This is meant to be used in combination with respond, which listens for the alert and displays an error or notification message on the appropriate control(s).

{ alert : String
, fail : Bool
, message : String
, class : String
}
-> Control context state delta output
-> Control context state delta output

Display an error or notification message on a Control in response to an alert emitted by another Control. If desired, cause validation to fail.

import Control exposing (Control, respond, int)


int
    |> respond
        { alert = "int-greater-than-zero"
        , fail = True
        , message = "Must be less than zero"
        , class = "control-feedback-fail"
        }


--: Control context String String Int

This is meant to be used in combination with alertIf, which emits the alert.

List validation

(output -> List Int)
-> String
-> Control context (List_ state) (List_ delta) output
-> Control context (List_ state) (List_ delta) output

Conditionally display an error on one or more items in a list control, based on the output of the list control.

The following example will display errors on the first two items in a list of strings, if and only if those first two items are "hello" and "world":

import Control exposing (Control, List_, alertAtIndexes, list, respond, string)


myString =
    string
        |> respond
            { alert = "no-hello-world"
            , fail = True
            , message = "List can't start with \"hello\" and \"world\"."
            , class = "control-feedback-fail"
            }


list myString
    |> alertAtIndexes
        (\list_ ->
            case list_ of
                "hello" :: "world" :: _ ->
                    [ 0, 1 ]


                _ ->
                    []
        )
        "no-hello-world"


--: Control context (List_ String) (List_ String) (List String)

Configuring controls

output
-> Control context state delta output
-> Control context state delta output

Set a default output value for a Control.

import Control exposing (Control, Tuple, default, int, string, tuple)


tuple int string
    |> default ( 1, "hello" )


--: Control context (Tuple String String) (Tuple String String) ( Int, String )
Float
-> Control context state delta output
-> Control context state delta output

Debounce a Control for a number of milliseconds before it will display any parsing or validation errors. This can be used to allow the user to finish typing into a text input before we show them feedback.

import Control exposing (Control, string, debounce)


string
    |> debounce 2000


--: Control context String String String
id :
String
-> Control context state delta output
-> Control context state delta output

Set an id for a Control.

import Control exposing (Control, string, id)


string
    |> id "my-string"


--: Control context String String String
String
-> Control context state delta output
-> Control context state delta output

Set a name for a Control.

import Control exposing (Control, string, name)


string
    |> name "My string"


--: Control context String String String
String
-> AdvancedControl context input state delta output
-> AdvancedControl context input state delta output

Set a label for a Control.

import Control exposing (Control, string, label)


string
    |> label "Enter your name"


--: Control context String String String
String
-> Control context state delta output
-> Control context state delta output

Add an HTML class attribute for a Control. See docs for Html.Attributes.class in elm-html.

import Control exposing (Control, string, class)


string
    |> class "important"
    |> class "no-really"


--: Control context String String String
List ( String, Bool )
-> Control context state delta output
-> Control context state delta output

Add a list of HTML class attributes to a Control. See docs for Html.Attributes.classList in the elm/html package.

import Control exposing (Control, string, classList)


string
    |> classList
        [ ( "important", True )
        , ( "no-really", True )
        , ( "ignore-me", False )
        ]


--: Control context String String String
(List (Html (Delta delta)) -> List (Html (Delta delta)))
-> Control context state delta output
-> Control context state delta output

Transform the HTML output of a Control's view function.

import Control exposing (Control, int, wrapView)
import Html


int
    |> wrapView (\view -> [ Html.div [] view ])


--: Control context String String Int

Forms with context

For some advanced use cases, you may want to create a form that knows about more than its own internal state. It may also need to know about some state that lives elsewhere in your app's Model.

This is where the ...WithContext family of types and functions come into play. They allow you to inject some external state into your form controls by passing a context value as an additional argument to the form's update, view, subscriptions and submit functions.

You can also pass context into any validations you apply to your Controls using failIfWithContext, noteIfWithContext etc.

type alias FormWithContext context state delta output msg =
{ blank : ( State state, Cmd msg )
, prefill : output -> ( State state, Cmd msg )
, update :
context -> Delta delta -> State state -> ( State state, Cmd msg )
, view : context -> State state -> Html msg
, subscriptions : context -> State state -> Sub msg
, submit :
context
-> State state
-> ( State state, Result (List Feedback) output )
}

A FormWithContext is a record which contains functions that you can use in your Elm Program to initialise, view, update, subscribe, and submit your form.

Unlike the standard Form, several of the functions provided by FormWithContext take an extra context parameter, which allows you to feed external data (e.g. from your app's Model) into your form and allow the form to respond accordingly.

For example, imagine your app's Model contains a list of users, and you build a form to create a new user. The new user should only be valid if their name hasn't already been chosen by another user.

We can ensure this by passing the set of existing user names into the form's update and submit functions as context:

import Control exposing (Control, FormWithContext, State, Delta, string, failIfWithContext, simpleFormWithContext)
import Set


type alias User =
    String


type alias Model =
    { formState : State String
    , existingUsers : Set.Set String
    }


type alias Context = 
    Set.Set String


type Msg
    = FormUpdated (Delta String)
    | FormSubmitted


userControl =
    string
        |> failIfWithContext
            (\existingUsers newUser ->
                Set.member newUser existingUsers
            )
            "User already exists"


userControl 


--: Control Context String String String


form =
    simpleFormWithContext
        { control = userControl
        , onUpdate = FormUpdated
        , onSubmit = FormSubmitted
        }


form


--: FormWithContext Context String String String Msg


update msg model =
    let
        context = 
            model.existingUsers
    in
    case msg of
        FormUpdated delta ->
            let
                (formState, cmd) =
                    form.update context delta model.formState
            in
            ( { model | formState = formState }, cmd )


        FormSubmitted -> 
            let
                (newFormState, formResult) = 
                    form.submit context model.formState
            in
            case formResult of
                Ok user -> 
                    let 
                     ( blankFormState, cmd ) =
                        form.blank
                    in
                    ( { model 
                        | existingUsers = Set.insert user model.existingUsers 
                        , formState = blankFormState
                      }
                    , cmd
                    )


                Err err ->
                    ( { model | formState = newFormState }
                    , Cmd.none 
                    )
{ control : Control context state delta output
, onUpdate : Delta delta -> msg
, onSubmit : msg
}
-> FormWithContext context state delta output msg

Convert a Control into a simple FormWithContext, rendered as a list of controls wrapped in an HTML <form> element, with a submit button at the bottom.

To use this function, follow the instructions in the documentation for simpleForm. The only difference is that when you call mySimpleForm.update, mySimpleForm.submit, mySimpleForm.view and mySimpleForm.subscriptions, you will need to supply a context value as the first argument.

{ control : Control context state delta output
, onUpdate : Delta delta -> msg
, view : List (Html msg) -> Html msg
}
-> FormWithContext context state delta output msg

Convert a Control into a FormWithContext, and render the form however you like.

To use this function, follow the instructions in the documentation for form. The only difference is that when you call myForm.update, myForm.submit, myForm.view and myForm.subscriptions, you will need to supply a context value as the first argument.

type alias DefinitionWithContext context state delta output =
{ blank : ( state, Cmd delta )
, prefill : output -> ( state, Cmd delta )
, update : context -> delta -> state -> ( state, Cmd delta )
, view :
context
-> { label : String
, id : String
, name : String
, class : String
, state : state
}
-> List (Html delta)
, subscriptions : context -> state -> Sub delta
, parse : context -> state -> Result (List String) output
, label : String
}

Definition for a custom control that is aware of its context.

This is exactly like the standard Definition type, except that the update, view, subscriptions and parse functions all take an additional context as their first argument.

DefinitionWithContext context state delta output
-> Control context state delta output

Define a new type of Control, with any arbitrary state, delta and output types, and that is aware of whatever context you choose to supply.

Here's a silly version of the counter example where we use a context to supply maximum and minimum bounds that the counter should stay within. We then implement various measures in the update, view, parse and subscriptions functions to try to prevent the count from going outside those bounds.

import Control exposing (Control, defineWithContext)
import Html
import Html.Attributes
import Html.Events
import Time


type CounterDelta
    = Increment
    | Decrement


type alias Context =
    { maxCount : Int, minCount : Int }


defineWithContext
    { blank = ( 0, Cmd.none )
    , prefill = \x -> ( x, Cmd.none )
    , update =
        \context delta state ->
            -- When an increment/decrement message comes in, check whether
            -- it would cause the count to go beyond the bounds set by the
            -- context
            (case delta of
                Increment -> if state < context.maxCount then state + 1 else state
                Decrement -> if state > context.minCount then state - 1 else state
            , Cmd.none
            )
    , view =
        \context { state, name, id, label, class } ->
            -- Disable the increment/decrement buttons if they would 
            -- cause the count to go beyond the bounds set by the context
            [ Html.div [ Html.Attributes.class class ]
                [ Html.label
                    [ Html.Attributes.for id ]
                    [ Html.text label ]
                , Html.div
                    [ Html.Attributes.id id
                    , Html.Attributes.name name
                    ]
                    [ Html.button
                        [ Html.Attributes.type_ "button"
                        , Html.Events.onClick Increment
                        , Html.Attributes.disabled (state >= context.maxCount)
                        ]
                        [ Html.text "+" ]
                    , Html.div [] [ Html.text <| String.fromInt state ]
                    , Html.button
                        [ Html.Attributes.type_ "button"
                        , Html.Events.onClick Decrement
                        , Html.Attributes.disabled (state <= context.minCount)
                        ]
                        [ Html.text "-" ]
                    ]
                ]
            ]
    , subscriptions =
        \context state ->
            -- Every 1 second, increment or decrement the count until 
            -- it's back within the bounds set by the context
            if state > context.maxCount then
                Time.every 1000 (\_ -> Decrement)
            else if state < context.minCount then
                Time.every 1000 (\_ -> Increment)
            else 
                Sub.none
    , parse =
        \context state ->
            -- Fail validation if the count is outside the bounds set
            -- by the context
            if state > context.maxCount || state < context.minCount then
                Err [ "count is out of bounds!" ]


            else
                Ok state
    , label = "Counter"
    }


--: Control Context Int CounterDelta Int
(context -> output -> Bool)
-> String
-> Control context state delta output
-> Control context state delta output

Display an error on a Control if the supplied predicate function returns True.

This causes the Control to fail validation.

import Control exposing (Control, failIfWithContext, int)


type alias Context =
    { minimumValue : Int }


int
    |> failIfWithContext
        (\context x -> x < context.minimumValue)
        "This is less than the minimum value!"


--: Control Context String String Int
(context -> output -> Bool)
-> String
-> Control context state delta output
-> Control context state delta output

Display a notification on a Control if the supplied predicate function returns True.

This does not cause the Control to fail validation.

import Control exposing (Control, int, noteIfWithContext)


type alias Context =
    { recommendedMinimumValue : Int }


int
    |> noteIfWithContext
        (\context x -> x < context.recommendedMinimumValue)
        "This value is below the recommended minimum - are you sure?"


--: Control Context String String Int
(context -> output -> Bool)
-> String
-> Control context state delta output
-> Control context state delta output

Emit an alert from a Control if the supplied predicate function returns True; another control can then react to this alert by using respond.

alertIfWithContext works exactly like alertIf, except that its predicate function takes a context as its first argument.

(context -> output -> List Int)
-> String
-> Control context (List_ state) (List_ delta) output
-> Control context (List_ state) (List_ delta) output

Conditionally display an error on one or more items in a list control, based on the output of the list control and the supplied context.

State and Delta types

These are types that you will see in your form's State and Delta type signatures.

type State state

State is the form's equivalent of an Elm program's Model type. It is used to manage the form's internal state.

type Delta delta
= NoDelta
| StateChangedByInput delta
| StateChangedInternally delta
| DebounceTimerSet Posix
| DebounceTimerExpired Posix
| TagSelected Int

A Delta is the form's equivalent of an Elm program's Msg type. It is used internally within a form to update its state.

Internal stuff that you can ignore

A user of this package shouldn't need to know about any of these types - they are only exposed to make it possible to write type signatures.

type AdvancedControl context input state delta output

A slightly more flexible version of Control, which is only used internally. As a user of the package, you shouldn't need to worry about this type.

type ControlFns context input state delta output

Some internal stuff needed to build up record and custom type controls

type Alert

Some internal stuff needed to handle validations

type RecordFns context input state delta output recordOutput

Some internal stuff needed to build up a record control

type Status

An internal type needed to represent the validation state of a control

type alias InternalViewConfig state =
{ state : state
, status : Status
, alerts : List Alert
, selected : Int
, name : String
, id : String
, label : String
, class : List String
}

Some internal stuff needed to view controls

type alias Path =
Path

An internal type needed to track the position of a control within a tree of controls

type alias Feedback =
{ path : Path
, label : String
, message : String
, class : String
, fail : Bool
}

Some internal stuff to handle validation

type End

Under the hood, this library encodes complex Elm types (such as records and custom types) as nested tuples, which are used in both the State and Delta types for each Control. The End type is used to mark the end of a nested tuple.

For example, the State for this Elm type:

type alias User =
    { name : String, age : Int }

Could be encoded as:

( String, ( Int, End ) )