421anon/elm-flow/1.0.0

NOTE: the latest version is 6.0.2

elm-flow

A monadic interface for The Elm Architecture, bridging synchronous state modifications with asynchronous subscriptions via Channels.

This library merges concepts from two community packages:

Overview

Flow s a is a free monad that describes a computation which can:

  • Read and write application state s
  • Fire Elm Cmds and receive their results
  • Subscribe to channels (ports, WebSockets, timers, …) and resume when a value arrives
  • Run multiple branches concurrently via Batch

The runtime interpreter (Flow.Program) drives these computations inside a standard TEA update loop, so the rest of your Elm app stays unchanged.

Quick start

With elm-flow, you do not define a custom Msg type or an update function. Instead, your view handlers and subscriptions emit Flow values directly, and the library runtime interprets them.

import Flow exposing (Flow)
import Html
import Html.Events

type alias Model =
    { count : Int }

main : Flow.Program () Model Never
main =
    Flow.element
        { init = \_ -> ( { count = 0 }, Flow.none )
        , view = view
        , subscriptions = \_ -> Sub.none
        }

view : Model -> Html.Html (Flow Model ())
view model =
    Html.button
        [ Html.Events.onClick (Flow.modify (\m -> { m | count = m.count + 1 })) ]
        [ Html.text ("Count: " ++ String.fromInt model.count) ]

Channels

Channels connect external event sources (ports, WebSockets, timers) to a Flow pipeline. Use Flow.Channel.connect to build a channel from an Elm subscription port and an optional command port, then pass it to Flow.await (receive one value) or Flow.subscribe (handle values indefinitely).

-- In Ports.elm
port onMessage : (String -> msg) -> Sub msg
port send : String -> Cmd msg

myChannel : Channel Model String
myChannel =
    Flow.Channel.connect
        (\callback -> Ports.onMessage (\s -> callback s))
        (\_ -> Ports.send "subscribe")

listenLoop : Flow Model ()
listenLoop =
    Flow.subscribe
        (\msg -> Flow.modify (\m -> { m | lastMessage = msg }))
        myChannel

Optics helpers

When you have nested state, the optics helpers (try, forAll, over, setAll, via) work with erlandsona/elm-accessors lenses to target sub-fields without manual getter/setter boilerplate.

API reference

See the generated documentation for the full API.

Core

FunctionDescription
pureLift a plain value into Flow
liftLift a Cmd into Flow
get / set / modifyRead and write the model
andThen / mapSequence and transform computations
batch / batchMRun multiple branches
noneTerminate / no-op

Async

FunctionDescription
awaitSuspend until a Channel delivers one value
subscribeHandle every value from a Channel indefinitely
yieldForce a render cycle before continuing
asyncFire-and-forget a sub-computation

Control flow

FunctionDescription
whenConditional execution
bracket_Acquire/release resources around a computation
settingTemporarily set a Bool lens to True
lockingSkip execution if a Bool lens is already True

License

BSD-3-Clause