-
-
Save z5h/41ca436679591b6c3e51 to your computer and use it in GitHub Desktop.
module TimeApp ( start, Config, App ) where | |
{-| 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 Html exposing (Html) | |
import Task | |
import Effects exposing (Effects, Never) | |
import Time exposing (Time) | |
{-| 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 action = | |
{ init : (model, Effects action) | |
, update : action -> Time -> model -> (model, Effects action) | |
, view : Signal.Address action -> model -> Html | |
, inputs : List (Signal.Signal action) | |
} | |
{-| An `App` is made up of a couple signals: | |
* `html` — a signal of `Html` representing the current visual | |
representation of your app. This should be fed into `main`. | |
* `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 = | |
{ html : Signal Html | |
, 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 action -> App model | |
start config = | |
let | |
singleton action = [ action ] | |
-- messages : Signal.Mailbox (List action) | |
messages = | |
Signal.mailbox [] | |
-- address : Signal.Address action | |
address = | |
Signal.forwardTo messages.address singleton | |
-- updateStep : (action, Time) (model, Effects action) -> (model, Effects action) | |
updateStep (action, time) (oldModel, accumulatedEffects) = | |
let | |
(newModel, additionalEffects) = config.update action time oldModel | |
in | |
(newModel, Effects.batch [accumulatedEffects, additionalEffects]) | |
-- update : (Time, (List action)) -> (model, Effects action) -> (model, Effects action) | |
update (time, actions) (model, _) = | |
List.foldl updateStep (model, Effects.none) (List.map (\a -> (a, time)) actions) | |
-- inputs : Signal (List action) | |
inputs = | |
Signal.mergeMany (messages.signal :: List.map (Signal.map singleton) config.inputs) | |
-- inputsWithTime : Signal (Time, (List action)) | |
inputsWithTime = | |
Time.timestamp inputs | |
-- effectsAndModel : Signal (model, Effects action) | |
effectsAndModel = | |
Signal.foldp update config.init inputsWithTime | |
model = | |
Signal.map fst effectsAndModel | |
in | |
{ html = Signal.map (config.view address) model | |
, model = model | |
, tasks = Signal.map (Effects.toTask messages.address << snd) effectsAndModel | |
} |
Thanks for this!
Would you be willing to publish this as an Elm package?
@metaraine I had opened an issue (some time ago) in the hopes of adding this to the official start-app. I've received no official response.
https://github.com/evancz/start-app/issues/24
I'll try to add some documentation and publish it before the weekend.
Awesome, thanks!
Yeah, the signature of TimeApp.start
is different enough from StartApp.start
that I don't know what to do. I haven't used Effects.
My main:
main : Signal Html
main = StartApp.start { model = model, view = view, update = update }
Suggested main:
app = TimeApp.start { init = ?, inputs = ? view = view, update = update }
app = main.html
@ethagnawl @prt2121 @jorgebg @doppioslash @simonewebdesign thanks for the stars. I've published this GIST as an elm package here http://package.elm-lang.org/packages/z5h/time-app/1.0.1/
In case Action and Time are coupled why not use (Action, Time) -> Model -> (Model, Effects Action) signature?
Note that your update function is now:
which means it gets a timestamp for every action. Handy if it's important to keep track of when actions occurred.