Introduction to

Elm language

 

Trajche Shoposki

Frontend Developer @

tshoposki@melontech.com

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 Errors

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 2018",
	"best coffee in Skopje",
	"startup tips"
];


const suggestedSearch = `search ${searchHistory[0]} again`;
const searchHistory = [];


const suggestedSearch = `search ${searchHistory[0]} again`;
searchHistory =
    [ "skopje marathon 2018"
    , "best coffee in Skopje"
    , "startup tips"
    ]


suggestedSearch =
    "search '" ++ List.head searchHistory ++ "' again"
searchHistory =
    [ "skopje marathon 2018"
    , "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 2018"
    , "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

 

 

ANY QUESTIONS?