arowM / elm-update-builder / Update

Module providing a way to build scalable update function. It enables you to build a complex update function by composing primitive functions.

Core


type Update model msg

Alternative type for update function.

update : msg -> Update model msg

run : Update model msg -> model -> ( model, Platform.Cmd.Cmd msg )

Evaluate to a model-cmd pair.

It is for converting to the normal update function. e.g.,

main : Program flags model msg
main =
    element
        { init = init
        , view = view
        , update = Update.run << update
        , subscriptions = subscriptions
        }

update : msg -> Update model msg
update =
    ...

batch : List (Update model msg) -> Update model msg

When you need to evaluate a couple Updates, you can batch them together. Each is evaluated from top to bottom.

type alias Model = { foo : String }

myUpdate : Update Model msg
myUpdate =
    batch
        [ modify (\m -> { m | foo = m.foo ++ "-1" })
        , modify (\m -> { m | foo = m.foo ++ "-2" })
        ]

run myUpdate { foo = "bar" }
    |> Tuple.first
--> { foo = "bar-1-2" }

Primitive Updates

none : Update model msg

Do nothing.

modify : (model -> model) -> Update model msg

Primitive Update that modifies Model.

push : (model -> Platform.Cmd.Cmd msg) -> Update model msg

Primitive Update that pushes a Cmd.

init : (model -> ( model, Platform.Cmd.Cmd msg )) -> Update model msg

An Update to initialize.

init f =
    batch
        [ modify (f >> Tuple.first)
        , push (f >> Tuple.second)
        ]

Handle conditions

when : Basics.Bool -> List (Update model msg) -> Update model msg

Evaluate Update only if it meets the condition.

unless : Basics.Bool -> List (Update model msg) -> Update model msg

Evaluate Update unless it meets the condition.

whenNothing : Maybe a -> List (Update model msg) -> Update model msg

Evaluate Update only if the first argument value is Nothing.

whenJust : Maybe a -> List (a -> Update model msg) -> Update model msg

Evaluate Update only if the first argument value is Just.

whenOk : Result err a -> List (a -> Update model msg) -> Update model msg

Evaluate Update only if the first argument value is Ok.

whenErr : Result err a -> List (err -> Update model msg) -> Update model msg

Evaluate Update only if the first argument value is Err.

Handle cases

with : (model -> a) -> List (a -> Update model msg) -> Update model msg

Branch the update process according to the situation.

type alias Model =
    { foo : String
    , bar : Maybe Int
    }

updateWithFoo : Update Model msg
updateWithFoo =
    with .foo
        [ on String.isEmpty
            [ Update.modify <| \model ->
                { model | foo = "empty" }
            ]
        ]

updateWithBar : Update Model msg
updateWithBar =
    with .bar
        [ onJust
            [ \n -> Update.modify <| \model ->
                { model | foo = String.fromInt n }
            ]
        ]


updateWithBarSequentially : Update Model msg
updateWithBarSequentially =
    with .bar
        [ onJust
            [ \n -> Update.modify <| \model ->
                { model | bar = Nothing }
            ]
        , onJust
            [ \_ -> Update.modify <| \model ->
                { model | foo = "This Update is not evaluated" }
            ]
        , onNothing
            [ Update.modify <| \model ->
                { model | foo = "bar has changed" }
            ]
        ]


run updateWithFoo
    { foo = ""
    , bar = Nothing
    }
        |> Tuple.first
--> { foo = "empty", bar = Nothing }

run updateWithFoo
    { foo = "foo"
    , bar = Nothing
    }
        |> Tuple.first
--> { foo = "foo", bar = Nothing }

run updateWithBar
    { foo = "foo"
    , bar = Just 4
    }
        |> Tuple.first
--> { foo = "4", bar = Just 4 }

run updateWithBarSequentially
    { foo = "foo"
    , bar = Just 4
    }
        |> Tuple.first
--> { foo = "bar has changed", bar = Nothing }

on : (a -> Basics.Bool) -> List (Update model msg) -> a -> Update model msg

Evaluate Update only if it matches the case.

onNothing : List (Update model msg) -> Maybe a -> Update model msg

Evaluate Update only if the case is Nothing.

onJust : List (a -> Update model msg) -> Maybe a -> Update model msg

Evaluate Update only if the case is Just.

onOk : List (a -> Update model msg) -> Result err a -> Update model msg

Evaluate Update only if the case is Ok.

onErr : List (err -> Update model msg) -> Result err a -> Update model msg

Evaluate Update only if the case is Err.

Lower level functions

map : (a -> b) -> Update model a -> Update model b

child : (b -> a) -> (a -> b -> b) -> Update a msg -> Update b msg

Lift Update for a child model to one for the parent model.

e.g.,

type alias Model =
    { child1 : ChildModel
    }

type alias ChildModel =
    { foo : String
    }

child1Update : Update ChildModel msg
child1Update =
    Debug.todo "Update for the `ChildModel`"

update : Update Model msg
update =
    child
        .child1
        (\c model -> { model | child1 = c })
        child1Update

maybeChild : (b -> Maybe a) -> (a -> b -> b) -> Update a msg -> Update b msg

Same as child but do nothing if first argument becomes to Nothing. It is useful for handling page updates of SPA.

e.g.,

type alias Model =
    { page : Page
    }

type Page
    = Page1 Page1Model
    | Page2 Page2Model

type alias Page1Model =
    { foo : String
    }

type alias Page2Model =
    { bar : Int
    }

page1Update : Update Page1Model msg
page1Update =
    Debug.todo "update function for `Page1`."

update : Update Model msg
update =
    Update.with identity
        [ \{ page } ->
            case page of
                Page1 page1 ->
                    page1Update
                        (\model ->
                            case model.page of
                                Page1 a ->
                                    Just a

                                _ ->
                                    Nothing
                        )
                        (\p model ->
                            case model.page of
                                Page1 _ ->
                                    { model | page = Page1 p }

                                _ ->
                                    model
                        )
                        page1Update

                _ ->
                    Debug.todo ""
        ]