laserpants / elm-burrito-update / Burrito.Update

Monadic-style interface for state updates.

Update


type alias Update a msg t =
( a
, List (Platform.Cmd.Cmd msg)
, List t 
)

Type wrapper for Elm's ( model, Cmd msg ) tuple.

save : a -> Update a msg t

Lifts a value into the Update context. For example,

save model

corresponds to ( model, Cmd.none ) in code that doesn't use this library.

addCmd : Platform.Cmd.Cmd msg -> a -> Update a msg t

Add a command to an Update pipeline. For example;

update msg state =
    case msg of
        SomeMsg someMsg ->
            state
                |> addCmd someCommand
                |> andThen (addCmd someOtherCommand)
                |> andThen (setStatus Done)

In this example, andThen (addCmd someOtherCommand) can also be shortened to andAddCmdsomeOtherCommand.

map : (a -> b) -> Update a msg t -> Update b msg t

Apply a function to the state portion of an Update.

mapCmd : (msg -> msg1) -> Update a msg t -> Update a msg1 t

Map over the Cmd contained in the provided Update.

join : Update (Update a msg t) msg t -> Update a msg t

Remove one level of monadic structure. It may suffice to know that some other functions in this library are implemented in terms of join. In particular, andThen f = join << map f

kleisli : (b -> Update c msg t) -> (a -> Update b msg t) -> a -> Update c msg t

Right-to-left (Kleisli) composition of two functions that return Update values, passing the state part of the first return value to the second function.

when : Basics.Bool -> (a -> Update a msg t) -> a -> Update a msg t

Run an update if the given condition is True, otherwise do nothing.

Chaining Updates

andThen : (b -> Update a msg t) -> Update b msg t -> Update a msg t

Sequential composition of updates. This function is especially useful in conjunction with the forward pipe operator (|>), for writing code in the style of pipelines. To chain updates, we compose functions of the form State -> Update State msg t:

say : String -> State -> Update State msg t
say what state = ...

save state
    |> andThen (say "hello")
    |> andThen doSomethingElse

Aside: andThen is like the monadic bind (>>=) operator in Haskell, but with the arguments interchanged.

sequence : List (a -> Update a msg t) -> a -> Update a msg t

Take a list of a -> Update a msg t values and run them sequentially, in a left-to-right manner.

Applicative Interface

These functions address the need to map functions having more than one argument over some Updates.

andMap : Update a msg t -> Update (a -> b) msg t -> Update b msg t

Trying to map a function number -> number -> number over two Updates , applying the first value

map (+) (save 4)

we end up with a result of type Update (number -> number) msg t. To apply the function inside this value to another Update number msg t value, we can write…

map (+) (save 4) |> andMap (save 5)

in elm repl, we can verify that the result is what we expect:

> (map (+) (save 4) |> andMap (save 5)) == save 9
True : Bool

This pattern scales in a nice way to functions of any number of arguments:

let
    f x y z =
        x + y + z
in
map f (save 1)
    |> andMap (save 1)
    |> andMap (save 1)

If not sooner, you'll need this when you want to mapN and N > 7.

See also map2, map3, etc.

ap : Update (a -> b) msg t -> Update a msg t -> Update b msg t

This function is like andMap, but with the arguments interchanged.

map2 : (p -> q -> r) -> Update p msg t1 -> Update q msg t1 -> Update r msg t1

Combine the state of two Updates using a function of two arguments. Equivalently, we can think of this as taking a function a -> b -> c and transforming it into a “lifted” function of type Update a msg t -> Update b msg t -> Update c msg t.

map3 : (p -> q -> r -> s) -> Update p msg t1 -> Update q msg t1 -> Update r msg t1 -> Update s msg t1

Combine the state of three Updates using a function of three arguments.

map4 : (p -> q -> r -> s -> t) -> Update p msg t1 -> Update q msg t1 -> Update r msg t1 -> Update s msg t1 -> Update t msg t1

Combine the state of four Updates using a function of four arguments.

map5 : (p -> q -> r -> s -> t -> u) -> Update p msg t1 -> Update q msg t1 -> Update r msg t1 -> Update s msg t1 -> Update t msg t1 -> Update u msg t1

Combine the state of five Updates using a function of five arguments.

map6 : (p -> q -> r -> s -> t -> u -> v) -> Update p msg t1 -> Update q msg t1 -> Update r msg t1 -> Update s msg t1 -> Update t msg t1 -> Update u msg t1 -> Update v msg t1

Combine the state of six Updates using a function of six arguments.

map7 : (p -> q -> r -> s -> t -> u -> v -> w) -> Update p msg t1 -> Update q msg t1 -> Update r msg t1 -> Update s msg t1 -> Update t msg t1 -> Update u msg t1 -> Update v msg t1 -> Update w msg t1

Combine the state of seven Updates using a function of seven arguments.

Callbacks

Callbacks allow for information to be passed up in the update tree.

apply : t -> a -> Update a msg t

Append a callback to the list of functions that subsequently get applied to the returned value using runCallbacks.

runCallbacks : Update a msg (a -> Update a msg t) -> Update a msg t

Compose and apply the list of monadic functions (callbacks) produced by a nested update call.

Program Integration

run : (p -> Update a msg t) -> p -> ( a, Platform.Cmd.Cmd msg )

Translate a function that returns an Update into one that returns a plain ( model, cmd ) pair.

run2 : (p -> q -> Update a msg t) -> p -> q -> ( a, Platform.Cmd.Cmd msg )

Same as run, but for functions of two arguments.

run3 : (p -> q -> r -> Update a msg t) -> p -> q -> r -> ( a, Platform.Cmd.Cmd msg )

Same as run, but for functions of three arguments.

Pointfree Helpers

using : (a -> a -> b) -> a -> b

Combinator useful for pointfree style. For example;

nextPage state =
    goToPage (state.current + 1) state

can be changed to

nextPage =
    using (\{ current } -> goToPage (current + 1))

with : (a -> b) -> (b -> a -> c) -> a -> c

Combinator useful for pointfree style. For example, to get rid of the lambda in the following code;

update msg state =
    case msg of
        Click ->
            state
                |> updateSomething
                |> andThen (\s -> setCounterValue (s.counter + 1) s)

we can write:

update msg state =
    case msg of
        Click ->
            state
                |> updateSomething
                |> andThen (with .counter (setCounterValue << (+) 1))

Shortcuts

andApply : t -> Update a msg t -> Update a msg t

Shortcut for andThen << apply

andAddCmd : Platform.Cmd.Cmd msg -> Update a msg t -> Update a msg t

Shortcut for andThen << addCmd

andWith : (b -> c) -> (c -> b -> Update a msg t) -> Update b msg t -> Update a msg t

Shortcut for \fun -> andThen << with fun

andUsing : (b -> b -> Update a msg t) -> Update b msg t -> Update a msg t

Shortcut for andThen << using

andIf : Basics.Bool -> (a -> Update a msg t) -> Update a msg t -> Update a msg t

Shortcut for \cond -> andThen << when cond