Elm language
OX Skill Share
What is Elm lang?
the Elm logo, BTW
Elm is a functional language that compiles to JavaScript and is specifically for designing the frontend of your web app
Compiles to JavaScript
*.elm
application.js
Elm Compiler
React-like Virtual DOM
Redux-like state management
Immutable data
Strongly typed
[ ]
,
,
,
,
.combine()
Elm Architecture
Actions
Reducers
Store
View
Redux
dispatch
React
Actions
Reducers
Store
View
NgRx/Store
dispatch
Angular
Elm Architecture
Model
init
Elm Architecture
Html
Model
init
view
Elm Architecture
Msg
update
Html
Model
init
view
Model
Msg
update
Html
Model
init
view
Model
import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)
main =
Browser.sandbox
{ init = init
, view = view
, update = update
}
init =
{ count = 0 }
type Msg
= Increment
update Increment model =
{ model | count = model.count + 1 }
view model =
div []
[ text model.count,
button [ onClick Increment ]
[ text "Increment" ]
]
Elm Compiler
Great error messages
import Html exposing (div)
viewItem item =
div [] [ item ]
view items =
div [] (List.nap viewItem items)
module Main exposing (view, viewItem)
import Html exposing (div)
viewItem item =
div [] [ item ]
view items =
div [] (List.nap viewItem items)
==================================================================================
> elm make src/list-nap.elm
Detected errors in 1 module.
-- NAMING ERROR ----------------------------------------------- src/list-nap.elm
I cannot find a `List.nap` variable:
11| div [] (List.nap viewItem items)
^^^^^^^^
The `List` module does not expose a `nap` variable. These names seem close
though:
List.map
List.any
List.map2
List.map3
Hint: Read <https://elm-lang.org/0.19.0/imports> to see how `import`
declarations work in Elm.
toRepoNameLabel repository =
"Name: " + repository.name
> elm make src/types/binop-string-append.elm
Detected errors in 1 module.
-- TYPE MISMATCH ----------------------------- src/types/binop-string-append.elm
I cannot do addition with String values like this one:
2| "Name: " + repository.name
^^^^^^^^
The (+) operator only works with Int and Float values.
Hint: Switch to the (++) operator to append strings!
import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)
main =
Browser.sandbox
{ init = init
, view = view
, update = update
}
init =
{ count = 0 }
type Msg
= Increment
update Increment model =
{ model | count = model.count + 1 }
view model =
div []
[ text model.count,
button [ onClick Increment ]
[ text "Increment" ]
]
import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)
main =
Browser.sandbox
{ init = init
, view = view
, update = update
}
init =
{ count = 0 }
type Msg
= Increment
update Increment model =
{ model | count = model.count + 1 }
view model =
div []
[ text model.count,
button [ onClick Increment ]
[ text "Increment" ]
]
> elm make src/Main.elm
Detected errors in 1 module.
-- TYPE MISMATCH -------------------------------------------------- src/Main.elm
The 1st argument to `sandbox` is not what I expect:
7| Browser.sandbox
8|> { init = init
9|> , view = view
10|> , update = update
11|> }
This argument is a record of type:
{ init : { count : number }
, update : Msg -> { count : number } -> { count : number }
, view : { count : String } -> Html Msg
}
But `sandbox` needs the 1st argument to be:
{ init : { count : number }
, update : Msg -> { count : number } -> { count : number }
, view : { count : number } -> Html Msg
}
Hint: Try using String.toInt to convert it to an integer?
init =
{ count = 0 }
view model =
div []
[ text model.count,
button [ onClick Increment ]
[ text "Increment" ]
]
type alias Model =
{ count : Int }
init : Model
init =
{ count = 0 }
view : Model -> Html Msg
view model =
div []
[ text model.count,
button [ onClick Increment ]
[ text "Increment" ]
]
type alias Model =
{ count : Int }
init : Model
init =
{ count = 0 }
view : Model -> Html Msg
view model =
div []
[ text model.count,
button [ onClick Increment ]
[ text "Increment" ]
]
> elm make src/Main.elm
Detected errors in 1 module.
-- TYPE MISMATCH -------------------------------------------------- src/Main.elm
The 1st argument to `text` is not what I expect:
36| [ text model.count
^^^^^^^^^^^
The value at .count is a:
Int
But `text` needs the 1st argument to be:
String
Hint: Want to convert an Int into a String? Use the String.fromInt function!
const searchHistory = [
"skopje marathon 2020",
"best coffee in Skopje",
"startup tips"
];
const suggestedSearch = `search ${searchHistory[0]} again`;
const searchHistory = [];
const suggestedSearch = `search ${searchHistory[0]} again`;
searchHistory =
[ "skopje marathon 2020"
, "best coffee in Skopje"
, "startup tips"
]
suggestedSearch =
"search '" ++ List.head searchHistory ++ "' again"
searchHistory =
[ "skopje marathon 2020"
, "best coffee in Skopje"
, "startup tips"
]
suggestedSearch =
"search '" ++ List.head searchHistory ++ "' again"
type Maybe something
= Just something
| Nothing
type Maybe String
= Just String
| Nothing
searchHistory =
[ "skopje marathon 2020"
, "best coffee in Skopje"
, "startup tips"
]
suggestedSearch =
"search '" ++ List.head searchHistory ++ "' again"
suggestedSearch =
case List.head searchHistory of
Just latestHistory ->
"search '" ++ latestHistory ++ "' again"
Nothing ->
"no previous searches"
suggestedSearch =
case searchHistory of
latestHistory :: _ ->
"search '" ++ latestHistory ++ "' again"
[] ->
"no previous searches"
suggestedSearch =
case searchHistory of
latestHistory :: _ ->
"search '" ++ latestHistory ++ "' again"
Static Types
(and nil isn't a thing)
No Runtime Exceptions
Compiler Driven Development
type alias Model =
{ count : Int }
main =
Browser.sandbox
{ init = init
, view = view
, update = update
}
init : Model
init =
{ count = 0 }
type Msg
= Increment
update : Msg -> Model -> Model
update Increment model =
{ model | count = model.count + 1 }
view : Model -> Html Msg
view model =
div []
[ text <| String.fromInt model.count
, button [ onClick Increment ]
[ text "Increment" ]
]
view : Model -> Html Msg
view model =
div []
[ text <| String.fromInt model.count
, button [ onClick Increment ] [ text "Increment" ]
, button [ onClick Decrement ] [ text "Decrement" ]
]
view : Model -> Html Msg
view model =
div []
[ text <| String.fromInt model.count
, button [ onClick Increment ] [ text "Increment" ]
, button [ onClick Decrement ] [ text "Decrement" ]
]
type Msg
= Increment
| Decrement
update Increment model =
{ model | count = model.count + 1 }
type Msg
= Increment
| Decrement
update Increment model =
{ model | count = model.count + 1 }
update msg model =
case msg of
Increment ->
{ model | count = model.count + 1 }
Decrement ->
{ model | count = model.count - 1 }
type alias Model =
{ count : Int }
main =
Browser.sandbox
{ init = init
, view = view
, update = update
}
init : Model
init =
{ count = 0 }
type Msg
= Increment
| Decrement
update msg model =
case msg of
Increment ->
{ model | count = model.count + 1 }
Decrement ->
{ model | count = model.count - 1 }
view : Model -> Html Msg
view model =
div []
[ text <| String.fromInt model.count
, button [ onClick Increment ] [ text "Increment" ]
, button [ onClick Decrement ] [ text "Decrement" ]
]
Radical Simplicity
JavaScript Fatigue
Immutable Data
& Pure Functions
JavaScript
user.update(params)
Elm
newUser = update prams user
Immutable Data
JavaScript
user.update(params)
Elm
newUser = update prams user
Pure Functions
Msg
update
Html
Model
init
view
Browser.sandbox
Browser.application
Msg
update
Html
Model
init
view
Cmd
int : (Model, Cmd Msg)
init =
( { count = 0 }, Http.get GotData getData )
getData : Http.Request String
getData =
Http.getString "https://example.com/data"
type Msg
= Increment
| Decrement
| GotData (Result Http.Error String)
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
...
GotData (Ok data) ->
-- handle success
GotData (Error error) ->
-- handle error
A Real-World Comparison of
Front-End Frameworks with Benchmarks
Frontends at https://github.com/gothinkster/realworld (April 2018)
https://medium.freecodecamp.org/a-real-world-comparison-of-front-end-frameworks-with-benchmarks-2018-update-e5760fb4a962
#1 Performance
First meaningful paint (ms) — lower is better
#2 Asset Size
Smaller is better. Smaller assets means faster downloads!
?
THANK YOU
FOR
YOUR ATTENTION