From Elm 0.16 to Elm 0.17
The story of two migrations
Tech Lead @ Meetic


k.lebrun@meetic-corp.com
kevinlebrun
Berlin Clock
3-steps migration

Use a local Elm
$ ./node_modules/.bin/elm-package install
$ ./node_modules/.bin/elm-make Main.elm --output index.html
$ elm package install
$ elm make Main.elm --output index.html
Before
After
Use StartApp and Effects
type alias Seconds =
Int
type State
= On
| Off
type alias Model =
{ top : State
, line1 : List State
, line2 : List State
, line3 : List State
, line4 : List State
}
model : Seconds -> Model
view : Model -> Html
main : Signal Html
main =
Signal.map (view << model) tick
tick : Signal Seconds
tick =
Signal.map (round << Time.inSeconds) <| Time.every Time.second
+import StartApp
+import Task
+import Effects exposing (Effects, Never)
...
-view : Model -> Html
-view model =
+view : Signal.Address Seconds -> Model -> Html
+view address model =
...
-main : Signal Html
+app =
+ StartApp.start
+ { init = (model 0, Effects.none)
+ , view = view
+ , update = update
+ , inputs = [tick]
+ }
+
+
main =
- Signal.map (view << model) tick
+ app.html
+
+port tasks : Signal (Task.Task Never ())
+port tasks =
+ app.tasks
+
+
+update time _ =
+ (model time, Effects.none)
Migrate to Elm 0.17
"dependencies": {
- "elm-lang/core": "3.0.0 <= v < 4.0.0",
- "evancz/elm-effects": "2.0.1 <= v < 3.0.0",
- "evancz/elm-html": "4.0.2 <= v < 5.0.0",
- "evancz/start-app": "2.0.2 <= v < 3.0.0"
+ "elm-lang/core": "4.0.0 <= v < 5.0.0",
+ "elm-lang/html": "1.0.0 <= v < 2.0.0"
},
- "elm-version": "0.16.0 <= v < 0.17.0"
-}
+ "elm-version": "0.17.0 <= v < 0.18.0"
Start with dependencies
- "elm": "^0.16.0"
+ "elm": "^0.17.0"
package.json
elm-package.json
-module Main (..) where
+module Main exposing (..)
...
-import StartApp
-import Task
-import Effects exposing (Effects, Never)
+import Html.App as Html
Simplify the imports
-view : Signal.Address Seconds -> Model -> Html
-view address model =
+view : Model -> Html Seconds
+view model =
Update the views
-app =
- StartApp.start
- { init = (model 0, Effects.none)
+main =
+ Html.program
+ { init = (model 0, Cmd.none)
, view = view
, update = update
- , inputs = [tick]
+ , subscriptions = subscriptions
}
-main =
- app.html
-
-port tasks : Signal (Task.Task Never ())
-port tasks =
- app.tasks
-
-
update time _ =
- (model time, Effects.none)
+ (model time, Cmd.none)
Reduce the noise
-tick : Signal Seconds
-tick =
- Signal.map (round << Time.inSeconds) <| Time.every Time.second
+subscriptions : Model -> Sub Seconds
+subscriptions model =
+ Sub.map (round << Time.inSeconds) <| Time.every Time.second (\a -> a)
Transform signals into subscriptions
The result

Pocket Triage
New terms
-update : Action -> Model -> ( Model, Effects Action )
+update : Msg -> Model -> ( Model, Cmd Msg )
Compose views
- , Selector.view (Signal.forwardTo address Link) model.snapshot
+ , Html.App.map Link <| Selector.view model.snapshot
Transform signals into subscriptions
-keyboard : Signal Action
+keyboard : Sub Msg
keyboard =
let
- keyToAction key =
+ keyToMsg key =
if key == 13 then
Next
else
- Link (Selector.keyToAction key)
+ Link (Selector.keyToMsg key)
in
- Signal.map keyToAction Keyboard.presses
+ Keyboard.presses keyToMsg
+subscriptions model =
+ keyboard
Create commands from tasks
-getLinks : String -> Task Http.Error ()
+getLinks : String -> Task Http.Error (List Link)
getLinks token =
(Http.fromJson Link.decodeLinks
<| authed "GET" token "http://localhost:8080/links" Http.empty
)
`andThen` (\dict -> succeed (Dict.values dict))
- `andThen` (OnReceiveLinks >> Signal.send actions.address)
- `onError` (toString >> HttpError >> Signal.send actions.address)
+doGetLinks token =
+ Task.perform (toString >> HttpError) OnReceiveLinks <| getLinks token
Program flags I
- var app = Elm.embed(Elm.Triage, div, { getToken: token });
+ var app = Elm.Triage.embed(div, { token: token });
Program flags II
-app =
- StartApp.start
- { init = ( emptyModel, Effects.none )
+main : Program { token : Maybe String }
+main =
+ Html.App.programWithFlags
+ { init = init
, view = view
, update = update
- , inputs = [ keyboard, actions.signal ]
+ , subscriptions = subscriptions
}
+init flags =
+ let
+ cmd =
+ case flags.token of
+ Just token ->
+ doGetLinks token
+ Nothing ->
+ Cmd.none
+ in
+ ( emptyModel flags.token, cmd )
-port getToken : Maybe String
Outgoing ports I
update action model =
case action of
Next ->
...
- cmd = Effects.batch [deleteEffect token deleted, sendDeleted deleted]
+ cmd = Cmd.batch [doDelete token deleted, Ports.deletedLinks deleted]
...
-deleted : Signal.Mailbox (List Link)
-deleted =
- Signal.mailbox []
-sendDeleted links =
- Signal.send deleted.address links
- |> Task.map (always NoOp)
- |> Effects.task
-port deletedLinks : Signal (List Link)
-port deletedLinks =
- deleted.signal
Outgoing ports II
+port module Ports exposing (..)
+
+import Link exposing (Link)
+
+
+port deletedLinks : List Link -> Cmd msg
The result

Conclusion
Q/A


k.lebrun@meetic-corp.com
kevinlebrun
From Elm 0.16 to Elm 0.17
By Kevin Le Brun
From Elm 0.16 to Elm 0.17
The story of two migrations
- 1,460