OX Skill Share
the Elm logo, BTW
Elm is a functional language that compiles to JavaScript and is specifically for designing the frontend of your web app
*.elm
application.js
Elm Compiler
,
,
,
,
.combine()
Actions
Reducers
Store
View
Redux
dispatch
React
Actions
Reducers
Store
View
NgRx/Store
dispatch
Angular
Model
init
Html
Model
init
view
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"
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" ]
]
JavaScript Fatigue
JavaScript
user.update(params)
Elm
newUser = update prams user
JavaScript
user.update(params)
Elm
newUser = update prams user
Msg
update
Html
Model
init
view
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
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
First meaningful paint (ms) — lower is better
Smaller is better. Smaller assets means faster downloads!