In my previous article I have told you a lot of theory about Elm, but to make you see why I like it so much I want to walk you through the process of designing a component for one of my projects. Hopefully you will see how the design choices are influenced by the language and reflected in the code.
Goal
What I'm trying to build
I'm a player of Neptune's Pride - an online strategy game set in space where players fight over stars. I am hosting a tool that lets players record their games and renders animated replays (timelapses) of them in an .mp4 format. I want to use Elm to provide timelapses online, animated as an SVG controlled by a slider.
View
How I want the user to see it
There are tree core elements of Elm application design: model, view and controller. View is the part that translates your model into DOM elements that your user can see and interact with, so to design the view it's best to try and imagine what you want your users to experience. This is how I saw it for the online timelapse tool:
- The user enters the online timelapse page with a button from a game's page.
- The user sees a loading screen.
- The screen changes to contain an SVG picture of the starting state of the game and animation controls.
- The user can control which tick of the game is displayed with a slider.
- The user can play and pause the animation using a button.
These user stories will not only describe the design of the view, but also be a starting point for designing the model, and help with the controller. At this point I usually draw myself a sketch of how the page will look like, and I keep adjusting it as I progress through the design process.
Model
Turining your view into data
With some user stories and a rough idea of the layout of the component I proceed to design the model: a data structure that will hold the state of the app at all times. This can be done very well using Elm's type system, so I create a file scratchpad.elm
to hold my notes before I create the project. From the user stories we see that the model should contain:
- Timelapse data (possibly unloaded)
- Currently displayed tick
- State of the animation (Played/Paused)
Which translates to the following Elm code:
type alias Model =
{ stars : Stars
, tick : Int
, playing : Bool
}
This model is incomplete. First of all it will definitely get expanded as the project progresses, but for now it uses a type Stars
that has not been defined. In my game recorder I store data about stars by assigning each star a list of pairs indicating when the star got captured by which player. Lets use this representation here, but also take into account the possibilities that star data may have not yet been loaded or that loading may have failed with some error message:
type Stars
= Stars ( List Star )
| Loading
| Failed String
type alias Star =
{ x : Float
, y : Float
, owners : List ( Int, Int )
}
This is enough of a model for now. Knowing what data we operate on and what our users might want to do with it let's proceed to the controller design.
Controller
What makes stuff happen
This is the part that is responsible for reacting to events and updating the model. In Elm the controller is represented by an update
function that takes messages generated by the runtime system, but I also consider the initial processing to be a part of the controller.
First lets tackle the messages. They represent events that our application might update on, which are: downloading star data finishing, time progression, manually changing the slider and pressing the play/pause button. This leaves us with a small Msg
type, and we don't have to worry about implementing reactions to these messages at the design level.
type Msg
= SliderChange Float
| Tick Time.Posix
| PauseTrigger
| GotStars (Result Http.Error Stars)
The init
function can take some arguments and must return a tuple containing an initial model and a command to execute by the runtime. The initial model is straightforward for now:
defaultModel : Model
defaultModel =
{ stars = Loading
, tick = 0
, playing = False
}
The command to be executed should fetch the stars info for us, so we need an address to fetch it from. The address will be passed as an argument to the init
function so that we can reuse the same static application for animating many different games. The resulting function is as follows:
init : String -> ( Model, Cmd Msg )
init sourceURL = ( defaultModel, getStars sourceURL )
This concludes the design of the controller. We can proceed to make the application!
Design complete!
That's it for now
In this article I have shown an example of my design process when working with Elm. I hope you now have a better idea of how Elm encourages and supports design of the app with its application design model and rich type system. In the next article I will create an Elm project and start converting this scratchpad code into an actual working page component. See you soon!
Top comments (1)
Looking forward to the next article. Also going to give Neptune's Pride a shot!