Elm
Frontends for Backend developers

I KNOW EVERYTHING ABOUT FILM
I HAVE SEEN OVER 240 OF THEM
Frontend Development
- Strong Static Typing
- Easily Unit testable
- Low Barrier to Entry
- No Runtime Exceptions

Elm Development
- Strong Static Typing
- Easily Unit testable
- Low Barrier to Entry
- No Runtime Exceptions
- Purely Functional
- Exceptional Compile Errors
- Faster than Angular and React
- Enforced Semantic Versioning
Blazing Fast


Why do we like pointers?
- Pass by reference => less copying => fast
- Mutability
- null as a value
Why do we like pointers?
- Pass by reference => less copying => fast
- Mutability
- null as a value
Why don't we like pointers?
- Think about copy/pass by reference?
- Harder to reason about code
- null pointer exceptions
Why do we like pointers?
- Pass by reference => less copying => fast
- Mutability
- null as a value
Why don't we like pointers?
- Think about copy/pass by reference?
- Harder to reason about code
- null pointer exceptions
We like immutability!
- Always pass by reference
- Easy to reason about code
- NO null pointer exceptions
view : Model -> Html Msg
view : Model -> Html Msg
update : Msg -> Model -> Model
update : Msg -> Model -> Model -- original = curried
update : (Msg, Model) -> Model -- uncurried
update : Msg -> (Model -> Model) -- explicitly curried
type alias Model = Int
view : Model -> Html Msg
view model = div [] [text (toString model)]
type alias Model = Int
view : Model -> Html Msg
view model = div [] [text (toString model)]
type Msg = Increment | Decrement
update : Msg -> Model -> Model
update msg model = case msg of
Increment -> model + 1
Decrement -> model - 1
elm-make ./src/Buttons1.elm --output=./buttons1.js
==================================== ERRORS ====================================
-- MISSING PATTERNS ----------------------------------------- ./src/Buttons1.elm
This `case` does not have branches for all possibilities.
22|> case msg of
23|> Increment -> model + 1
You need to account for the following values:
Main.Decrement
Add a branch to cover this pattern!
If you are seeing this error for the first time, check out these hints:
<https://github.com/elm-lang/elm-compiler/blob/0.18.0/hints/missing-patterns.md>
The recommendations about wildcard patterns and `Debug.crash` are important!
Detected errors in 1 module.
make: *** [build] Error 1

import Html exposing (beginnerProgram, div, button, text, Html, span)
import Html.Events exposing (onClick)
main =
beginnerProgram { model = 0, view = view, update = update }
type alias Model = Int
view : Model -> Html Msg
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [text (toString model)]
, button [ onClick Increment ] [ text "+" ]
]
type Msg = Increment | Decrement
update : Msg -> Model -> Model
update msg model =
case msg of
Increment -> model + 1
Decrement -> model - 1
type alias Model = Int
view : Model -> Html Msg
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] (List.map viewStar (List.range 0 model))
, button [ onClick Increment ] [ text "+" ]
]
viewStar : Int -> Html Msg
viewStar index = span [style [("color", chooseColor (index % 4))]] [text "*"]
chooseColor : Int -> String
chooseColor c = case c of
0 -> "red"
1 -> "yellow"
2 -> "blue"
3 -> "green"
_ -> ""
type Msg = Increment | Decrement
update : Msg -> Model -> Model
update msg model =
case msg of
Increment -> model + 1
Decrement -> model - 1
word : String
word = "TreeBay"
charDict : Dict Int Char
charDict = String.toList word
|> List.indexedMap (\index char -> (index, char))
|> Dict.fromList
word : String
word = "TreeBay"
charDict : Dict Int Char
charDict = Dict.fromList
(List.indexedMap (\index char -> (index, char))
(String.toList word))
charDict : Dict Int Char
charDict = String.toList word
|> List.indexedMap (\index char -> (index, char))
|> Dict.fromList
charDict : Dict Int Char
charDict = Dict.fromList
<| List.indexedMap (\index char -> (index, char))
<| String.toList word
view : Model -> Html Msg
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] (List.map viewLetter (List.range 0 model))
, button [ onClick Increment ] [ text "+" ]
]
charDict : Dict Int Char
charDict = String.toList word
|> List.indexedMap (\index char -> (index, char))
|> Dict.fromList
viewLetter : Int -> Html Msg
viewLetter index = span [style [("color", chooseColor index)]]
[text (chooseLetter index)]
chooseLetter : Int -> String
chooseLetter index = case Dict.get (index % String.length word) charDict of
Nothing -> ""
(Just letter) -> String.fromChar letter
word : String
word = "TreeBay"
import Navigation
import UrlParser exposing (s, parsePath, top, (<?>), (</>), intParam)
main = Navigation.program (\_ -> Nop)
{ init = init
, view = view
, update = update
, subscriptions = (\_ -> Sub.none)
}
init : Navigation.Location -> (Model, Cmd Msg)
init location = let l = { location | pathname = "" }
in case parsePath (top <?> intParam "num") l of
(Just (Just n)) -> (n, Cmd.none)
_ -> (0, Cmd.none)
import Navigation
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Increment -> let newmodel = model + 1 in
(newmodel, Navigation.modifyUrl <| "?num=" ++ toString newmodel)
Decrement -> let newmodel = model - 1 in
(newmodel, Navigation.modifyUrl <| "?num=" ++ toString newmodel)
type alias Model = {
num : Int
, letters : Dict.Dict Int String
, word : String
, size : Int
}
newModel : Int -> String -> Model
newModel num word = Model num (newCharDict word) word (String.length word)
newCharDict : String -> Dict.Dict Int String
newCharDict word = Dict.fromList
<| List.indexedMap (\index char -> (index, String.fromChar char))
<| String.toList word
import Navigation
import UrlParser
exposing (s, parsePath, top, (<?>), (</>), intParam, stringParam, map, Parser)
init : Navigation.Location -> (Model, Cmd Msg)
init location =
let loc = { location | pathname = "" } -- we parse the arguments not the path
in case parsePath urlpath loc of
(Just model) -> (model, Cmd.none)
_ -> (newModel 0 "", Cmd.none)
parseModel : Maybe Int -> Maybe String -> Model
parseModel maybeNum maybeStr =
let num = Maybe.withDefault 0 maybeNum
word = Maybe.withDefault "" maybeStr
in newModel num word
urlSpec : Parser (Maybe Int -> Maybe String -> b) b
urlSpec = top <?> intParam "num" <?> stringParam "word"
urlpath : Parser (Model -> Model) Model
urlpath = UrlParser.map parseModel urlSpec
UrlParser.map : a -> Parser a b -> Parser (b -> c) c
UrlParser.parsePath : Parser (b -> c) c -> Location -> Maybe c
a : Maybe Int -> Maybe String -> Model
b : Model
c : Model
type Msg = Increment
| Decrement
| EditWord String
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Increment -> let newmodel = {
model | num = model.num + 1
} in (newmodel, seturl newmodel)
Decrement -> let newmodel = {
model | num = model.num - 1
} in (newmodel, seturl newmodel)
(EditWord w) -> let
newmodel = newModel model.num w
in (newmodel, seturl newmodel)
seturl : Model -> Cmd Msg
seturl {num, word} = Navigation.modifyUrl <| "?num=" ++ toString num ++ "&word=" ++ word
view : Model -> Html Msg
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] (List.map (viewLetter model) (List.range 0 model.num))
, button [ onClick Increment ] [ text "+" ]
, viewInput "Word" model.word
]
viewInput : String -> String -> Html Msg
viewInput name v = div [] [
label [] [text name]
, input [
Html.Attributes.type_ "text"
, onWithOptions "input" preventDefaults (Json.map EditWord targetValue)
, value v
] []
]
suite : Test
suite = describe "choose letter" [
test "letter" <| \_ -> Expect.equal (chooseLetter (newModel "TreeBay") 5) "a"
, test "wrap" <| \_ -> Expect.equal (chooseLetter (newModel "TreeBay") 11) "B"
, test "empty" <| \_ -> Expect.equal (chooseLetter (newModel "") 3) ""
]
$ elm-test
Success! Compiled 0 modules.
Successfully generated /dev/null
Success! Compiled 1 module.
Successfully generated
~/elm-example/elm-stuff/generated-code/elm-community/elm-test/elmTestOutput.js
elm-test 0.18.12
----------------
Running 3 tests. To reproduce these results, run: elm-test --fuzz 100 --seed 186127600
TEST RUN PASSED
Duration: 227 ms
Passed: 3
Failed: 0
suite : Test
suite = describe "choose letter" [
fuzz string "fuzz 5" <|
\randString -> Expect.equal
(testChooseLetter randString 5)
(chooseLetter (newModel randString) 5)
]
testChooseLetter : String -> Int -> String
testChooseLetter word index = if String.length word == 0 then ""
else let chars = Array.fromList <| String.toList word
in case Array.get (index % String.length word) chars of
Nothing -> ""
(Just j) -> String.fromChar j
suite : Test
suite = describe "choose letter" [
fuzz string "fuzz 5" <|
\randomlyGeneratedString -> Expect.equal
(testChooseLetter randomlyGeneratedString 5)
(chooseLetter (newModel randomlyGeneratedString) 5)
]
suite : Test
suite = describe "choose letter" [
fuzz2 string int "fuzzy" <|
\randString randIndex -> Expect.equal
(testChooseLetter randString randIndex)
(chooseLetter (newModel randString) randIndex)
]
testChooseLetter : String -> Int -> String
testChooseLetter word index = if String.length word == 0 then ""
else let chars = Array.fromList <| String.toList word
in case Array.get (index % String.length word) chars of
Nothing -> ""
(Just j) -> String.fromChar j
Low Barrier to Entry
- Learn it in a day https://guide.elm-lang.org/
- Real Life Experiment: Two Interns
- No M Word

Elm Development
- Strong Static Typing
- Easily Unit testable
- Low Barrier to Entry
- No Runtime Exceptions
- Purely Functional
- Exceptional Compile Errors
- Faster than Angular and React
- Enforced Semantic Versioning
Everything you like about backend development and more, now available in the Frontend.
Guide:
Contact:
waschulze@ebay.com
awalterschulze@gmail.com
#ecg-pure
Hi I am Walter. I am the backend developer on treeBay.
I needed to write a frontend for the treeBay editor and I chose Elm.
I really enjoyed writing and now maintaining code in Elm so much so that I now feel depressed when I write backend code.
I hope that with this talk I can explain why.
Elm Frontends for Backend developers
Elm frontends for backend developers
By Walter Schulze
Elm frontends for backend developers
- 1,495