Skip to content

Instantly share code, notes, and snippets.

@liamcurry
Created May 10, 2016 22:42
Show Gist options
  • Save liamcurry/b4e5cef06d8a32a50750ce459d6ee11d to your computer and use it in GitHub Desktop.
Save liamcurry/b4e5cef06d8a32a50750ce459d6ee11d to your computer and use it in GitHub Desktop.
module Start exposing (start, Config, App)
{-| This module helps you start your application in a typical Elm workflow.
It assumes you are following [the Elm Architecture][arch] and using
[elm-effects][]. From there it will wire everything up for you!
**Be sure to [read the Elm Architecture tutorial][arch] to learn how this all
works!**
[arch]: https://github.com/evancz/elm-architecture-tutorial
[elm-effects]: http://package.elm-lang.org/packages/evancz/elm-effects/latest
# Start your Application
@docs start, Config, App
-}
import Task
{-| The configuration of an app follows the basic model / update / view pattern
that you see in every Elm program.
The `init` transaction will give you an initial model and create any tasks that
are needed on start up.
The `update` and `view` fields describe how to step the model and view the
model.
The `inputs` field is for any external signals you might need. If you need to
get values from JavaScript, they will come in through a port as a signal which
you can pipe into your app as one of the `inputs`.
-}
type alias Config model msg =
{ init : ( model, Cmd msg )
, update : msg -> model -> ( model, Cmd msg )
, inputs : List (Signal.Signal msg)
}
{-| An `App` is made up of a couple signals:
* `model` — a signal representing the current model. Generally you
will not need this one, but it is there just in case. You will know if you
need this.
* `tasks` — a signal of tasks that need to get run. Your app is going
to be producing tasks in response to all sorts of events, so this needs to
be hooked up to a `port` to ensure they get run.
-}
type alias App model =
{ model : Signal model
, tasks : Signal (Task.Task Never ())
}
{-| Start an application. It requires a bit of wiring once you have created an
`App`. It should pretty much always look like this:
app =
start { init = init, view = view, update = update, inputs = [] }
main =
app.html
port tasks : Signal (Task.Task Never ())
port tasks =
app.tasks
So once we start the `App` we feed the HTML into `main` and feed the resulting
tasks into a `port` that will run them all.
-}
start : Config model msg -> App model
start config =
let
singleton msg =
[ msg ]
-- messages : Signal.Mailbox (List msg)
messages =
Signal.mailbox []
-- address : Signal.Address msg
address =
Signal.forwardTo messages.address singleton
-- updateStep : msg -> (model, Cmd msg) -> (model, Cmd msg)
updateStep msg ( oldModel, accumulatedEffects ) =
let
( newModel, additionalEffects ) =
config.update msg oldModel
in
( newModel, Cmd.batch [ accumulatedEffects, additionalEffects ] )
-- update : List msg -> (model, Cmd msg) -> (model, Cmd msg)
update actions ( model, _ ) =
List.foldl updateStep ( model, Cmd.none ) actions
-- inputs : Signal (List msg)
inputs =
Signal.mergeMany (messages.signal :: List.map (Signal.map singleton) config.inputs)
-- effectsAndModel : Signal (model, Cmd msg)
effectsAndModel =
Signal.foldp update config.init inputs
model =
Signal.map fst effectsAndModel
in
{ model = model
, tasks = Signal.map (Cmd.toTask messages.address << snd) effectsAndModel
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment