Elmtastic Updates
With the release of Elm 0.17 there were some fundamental changes to the Elm language. This post is my attempt to help those that may be struggling with these changes
I’ve played with lots of new languages over the last year or so namely Elixir, Rust, and Elm. Elm and Elixir have been my favorites and I hope to cover those much more in this and future blog posts.
So whats new in Elm 0.17 why do I need to upgrade anything? #
I’ll summarise here:
- Signals have been removed (Hence some upgrade is required if you had any code using signals.)
- Faster HTML renderer
- Libraries for geolocation, page visibility, and web sockets
- Generated JS is smaller and works with Google’s Closure Compiler
- Generated JS works with RequireJS and CommonJS
- Features in place for services like GraphQL and Elixir Phoenix
- Improved documentation at guide.elm-lang.org
- Helpful messages when decoding JSON fails
The big things are that are going to throw a spanner in the works are Signals have been removed and the following packages have moved around:
evancz/elm-html -> elm-lang/html
evancz/elm-svg -> elm-lang/svg
evancz/virtual-dom -> elm-lang/virtual-dom
evancz/start-app -> elm-lang/html
evancz/elm-effects -> elm-lang/core
Be sure to read the official post on the subject for the full information.
How Do I upgrade? Are there any resources to help? #
There are a few guides already that do help, the official upgrade plan is really useful as is migrating from elm 0.16 to 0.17 and I would advise that you read the official plan before this or any other guides. Hopefully my whistle stop tour of upgrading an existing package may be of help.
Example Upgrade #
Lets take the elm-sprite package as an example, its fairly simple with only a few dependencies.
elm.package.json #
Lets look at the elm.package.json file:
{
"version": "1.0.0",
"summary": "Simple sprite rendering for elm-html",
"repository": "https://github.com/Fresheyeball/elm-sprite.git",
"license": "MIT",
"source-directories": [
"src"
],
"exposed-modules": [
"Sprite"
],
"dependencies": {
"elm-lang/core": "3.0.0 <= v < 4.0.0"
},
"elm-version": "0.16.0 <= v < 0.17.0"
}
We need to update the dependencies for elm-lang/core
to 4.0.1 <= v < 5.0.0
and elm-version
to 0.17.0 <= v < 0.18.0
. Pretty easy in terms of dependencies.
Sprite.elm #
This ones pretty easy, the only thing to change is the module definition which is using an obsolete syntax:
module Sprite (..) where
Now becomes
module Sprite exposing (..)
Actually the Elm compiler does a fantastic job here by actually telling us what the problem is:
-- SYNTAX PROBLEM --------------------------------------------------- Sprite.elm
I ran into something unexpected when parsing your code!
1| module Sprite (..) where
^
I am looking for one of the following things:
something like `exposing (..)` which replaced `where` in 0.17
whitespace
So that was pretty painless, lets have a look at the example file: One.elm
One.elm #
This ones a bit more tricky as there are signals involved and packages that have moved about.
Lets have a look at the changes needed.
First of all lets address the obsolete where
syntax:
module One exposing (..)
We need to remove the Signal
, Html.Events
and Effects
imports packages, let’s remove these:
import Signal exposing (message, Address)
import Html.Events exposing (on, targetValue)
import Effects exposing (Effects, none)
Now we need to address the changes in the Time package as fps
is no longer available, we can use milliseconds instead.
import Time exposing (Time, millisecond)
Now we need to adjust the StartpApp
import and use Html.App
instead.
import Html.App as Html
So all in all the imports section will look like this:
module One exposing (..)
import Html exposing (..)
import Html.App as Html
import Time exposing (Time, millisecond)
import Html.Attributes as A
import Sprite exposing (..)
import Array
Action #
The first thing we will tackle is the Action which flows though this application. It now looks like this:
type Action
= Tick Time
As Action
has now been replaced with Msg
so we need to change this to the following:
type Msg
= Tick Time
init #
Next lets look at the init function its signature is slightly different now. Html.Program
now starts the application
init : (model, Cmd msg)
So rather than:
init : Sprite {}
init =
{ sheet = "https://10firstgames.files.wordpress.com/2012/02/actionstashhd.png"
, rows = 16
, columns = 16
, size = ( 2048, 2048 )
, frame = 0
, dope = idle
}
It will now become:
init : (Sprite {}, Cmd Msg)
init = (
{ sheet = "https://10firstgames.files.wordpress.com/2012/02/actionstashhd.png"
, rows = 16
, columns = 16
, size = ( 2048, 2048 )
, frame = 0
, dope = idle
} , Cmd.none)
view #
This is what view looks like:
view : Address Action -> Sprite {} -> Html
view address s =
let
onInput address contentToValue =
on
"input"
targetValue
(message address << contentToValue)
in
div
[]
[ node
"sprite"
[ A.style (sprite s) ]
[]
]
At first glance this looks a bit more complex but when you look at the code a little
more you come to realise that the onInput
function is not even used anymore this is
just dead code. So no all that remains is to change view
to match the new Elm 0.17
architecture, so instead of Address Action -> Sprite {} -> Html
it will now be
Sprite {} -> Html Msg
as Address
and Action
are now no longer needed.
view : Sprite {} -> Html Msg
view s =
div
[]
[
node
"sprite"
[ A.style (sprite s)]
[]
]
update #
Ok, now for update
lets have a look at that:
update : Action -> Sprite {} -> ( Sprite {}, Effects Action )
update action s =
let
s' =
case action of
Tick _ ->
advance s
in
( s', none )
update
now has a signature of msg -> model -> (model, Cmd msg)
so all we really have
to do is replace Action
with Msg
, and Effects Action
with Cmd Msg
. Finally I
change the return to: Cmd.none
which was previously Effects.none
.
update : Msg -> Sprite {} -> (Sprite {}, Cmd Msg)
update action s =
let
s' =
case action of
Tick _ ->
advance s
in
( s', Cmd.none )
subs #
This part is new, with the old 0.16 based version there was a signal which was mapping
time to a sprite update:[ Signal.map Tick (fps 30) ]
. Now that we will be using
subscriptions this is a simple function like this:
subs : Sprite {} -> Sub Msg
subs model =
Time.every (millisecond * 33) Tick
So every 33 milliseconds (30 frames per second) we are sending a Tick command to the update function.
application start #
The last part is the application start, heres what it looks like in 0.16:
app : StartApp.App (Sprite {})
app =
StartApp.start
{ view = view
, update = update
, init = ( init, none )
, inputs = [ Signal.map Tick (fps 30) ]
}
main : Signal Html
main =
app.html
You can see the signal
I talked about above. This whole section has now become a lot simpler in Elm 0.17.
main : Program Never
main =
Html.program
{ view = view
, update = update
, init = init
, subscriptions = subs
}
StartApp
has now become Html.App
which we aliased to Html
at the beginning import Html.App as Html
and we use the program
function to feed in all the functions we just declared.
Ok, we’re all done, hopefully someone found this useful!
One tip I can give is to update the function signatures for each function first and it should make things a little clearer on what you need to do.
Until next time!