Picking up where I left off last time, I'll share my experience on moving from an ugly web-page to an elm-mdl based one and how to split the code and the update method (which doesn't seem that popular though)
Although the module elm-mdl proved itself rather nice to use, setting it up wasn't as easy. I followed this post to set up my app. The first step was to create a "property" on my main model to plug-in the mdl model. This is where elm-mdl is gonna work its magic and keep up with the state of things across the app. elm-mdl also provide us with an "initial" state for its module.
Next thing that we need to do to get ready to play with elm-mdl is to "tag" elm-mdl's messages so that anything coming up for an elm-mdl function in your code will be redirected to elm-mdl for handling, be transformed into whatever you asked for and be returned to you in a better message. We will do that by adding this:
You'll probably have to follow the same principle for the subscription if you start playing with Layout and things like that but elm-mdl explain it rather well and the previously mention post as well.
Then of course you need to remember to put these lines in your html file (and no I definitely did NOT scratched my head for a moment before I remember about it…)
Talking about the index.html file. I couldn't find a nice way to use a custom html file with elm-reactor, so instead I turned to elm-live for that. Bottom line: install it with npm or yarn and then use this line to start your app:
elm-live "your-file" --output=elm.js --open
and that's it! Elm-live will open your browser and reload as you make your changes! Ain't it cool?
One thing bothered me with the code as it was: having all the code in a bunch of big files, no matter how clear they were, was still not "clean code" for me. So I went out and tried to find guidelines on how to split the code and found out this link. Well I had to pause a bit after reading it actually. Splitting the original code into different files to separate the responsibilities was rather easy. I got 4 files:
- Types.elm: host the types used in our application (mainly Model and Msg)
- State.elm: host the state of our application. i.e. how to initialize the application, how to update it
- View.elm: host the HTML of our application
- App.elm: ties it all together!
Now, that was enough for my small game but still it would have been cleaner to extract each part of the application in a separate module. So for the sack of the exercise (and for the peace of my mind…) I split up the application on 2 parts: the setup and the actual game. I will not go into the detail of the implementation of each module: it is more or less copied and paste from the previous implementation however linking the pieces together was rather interesting and we'll check that out here.
First let's look at the "main" type file:
What we see in here is more or less a natural definition of our model: a record that host the model definition of each of its submodule. Each module in turn is responsible for hosting whichever properties they need in their model. But the most interesting part (IMO at least) lies in the State.elm file where we will define the app's initial state and update function.
As you can see on the init, this code is rather simple yet verbose. For each direct child-module, our current modul need to pull the init state and add it to it's own init. This could be automatically written though, I hope to have some time to dig in Atomist to try making that happen. Then the update method is just a dispatcher that forward each message to its rightful submodule. It makes it rather easy to communicate between each submodule as they just need to trigger the right message to send something to another module: in this app for instance, the setup send message to let the board module now when it should be ticking or not. The counter point to that is that it requires our view to send Msg (i.e. the main message type).
The same observation can be seen on the Cmd part of our update method in the submodule. One could be tempted to "tag" the command like we do for the elm-mdl messages but then it would prevent or at least complicate the process of sending command to other modules.
Anyhow, enough of that. Have a look at the code if you've questions, suggestions I'll be happy to talk :)
Before closing this post, let's have a look at how manipulating SVG is seamless in Elm. For those who checked the code of my first post about elm you've seen something like this:
The view code that was responsible for that horror looking table was this:
Switching to SVG was seamless: in Elm SVG nodes are manipulated exactly like HTML ones! (provided you remember to elm package elm-lang/svg).
And we're done!
If you want to have a look at the app's current state: check this out