-
-
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.