Introduction to Elm

In the browser, only JavaScript

Compilation

17k LoC

200k LoC

eight = 5 + 3

Affectation

Try it online

eight = round 8.3

Function call

add a b = a + b

Function definition

add a b = a + b

-- call that function:
eight = add 7 1

Function definition

eight = add 7 1

add a b = a + b

Order of declarations does not matter

add : number -> number -> number
add a b = a + b

Types


add3 a b c = a + b + c

What is the type of add3?

add3:
    number
    -> number
    -> number
    -> number
add3 a b c = a + b + c

Good practice:
add type anotation!


user = 
    { name = "Seb"
    , score = 42
    , city = "Paris"
    }

Records

user : User
user = 
    { name = "Seb"
    , score = 42
    , city = "Paris"
    }

Records

type alias User =
    { name : String
    , score : Int
    , city : String
    }

Records


newUser =
    { user | score = 43 }


newUserBis =
    { user | score = user.score + 1 }

Record updates

newUser : User
newUser =
    { user | score = 43 }

newUserBis : User
newUserBis =
    { user | score = user.score + 1 }

Record updates

rewardUser : User -> User
-- increase the score by one

Record updates

rewardUser : User -> User
rewardUser user =
    { user | score = user.score + 1 }

Record updates

init : Model

view : Model -> Html Msg

update : Msg -> Model -> Model

The Elm Architecture

Kolja Wilcke

Counter example

Counter example

Challenge

Add a "reset button"

Error messages from the compiler

READ THEM!

[] -- empty list
[1, 2, 6] : List number
["Hello", "world"] : List String

Lists

[] -- empty list
[1, 2, 6] : List number
["Hello", "world"] : List String
["Hello", 42] -- impossible!

Lists are homogeneous




ages : List Int
ages = [ 18, 14, 35, 10, 51 ]

agesInFiveYears : List Int
agesInFiveYears =
    -- add five to each element

No for-loops!

addFive : Int -> Int
addFive x = 5 + x

ages : List Int
ages = [ 18, 14, 35, 10, 51 ]

agesInFiveYears : List Int
agesInFiveYears =
    List.map addFive ages

No for-loops!

List.map :
    (a -> b)
    -> List a
    -> List b



agesInFiveYears =
    List.map addFive ages

Map signature

List.map :
    (a -> b)   -- fn to apply
    -> List a  -- original list 
    -> List b  -- returned list



agesInFiveYears =
    List.map addFive ages

Map signature

ages : List Int
ages = [ 18, 14, 35, 10, 51 ]

-- transform into : 
-- [ "18 ans", "14 ans", ... ]

-- Hint:
-- String.fromInt : Int -> Str

Map challenge

ages : List Int
ages = [ 18, 14, 35, 10, 51 ]

displayAge : Int -> String
displayAge age =
    String.fromInt age ++ " ans"

agesDisplayed =
    List.map displayAge ages

Map challenge

addFive : Int -> Int
addFive x = 5 + x

ages : List Int
ages = [ 18, 14, 35, 10, 51 ]

agesInFiveYears : List Int
agesInFiveYears =
    List.map addFive ages

Anonymous functions




ages : List Int
ages = [ 18, 14, 35, 10, 51 ]

agesInFiveYears : List Int
agesInFiveYears =
    List.map (\x -> x + 5) ages

Anonymous functions




ages : List Int
ages = [ 18, 14, 35, 10, 51 ]

agesInFiveYears : List Int
agesInFiveYears =
    List.map (\x -> x + 5) ages

Anonymous functions

add3: number -> number -> number
add3 = 
    \ a b c -> a + b + c

Anonymous functions

ages : List Int
ages = [ 18, 14, 35, 10, 51 ]
-- transform into
-- <ul>
--    <li>18 ans</li>
--    <li>14 ans</li>
--    ...
-- </ul>

Map challenge 2

ul : List Attributes
     -> List Html msg
     -> Html msg

li : List Attributes
     -> List Html msg
     -> Html msg

text : String -> Html msg

Map challenge 2

ul [] 
    [ li [] [text ("18 ans")]
    , li [] [text ("14 ans")]
    ...
    ]

Map challenge 2

ul [] agesInLis

agesInLis : List (Html msg)
agesInLis = 
    []

Map challenge 2

ul [] agesInLis

agesInLis : List (Html msg)
agesInLis = 
    List.map ageToLi ages

ageToLi : Int -> Html msg
ageToLi age =
    ...

Map challenge 2

ageToLi : Int -> Html msg
ageToLi age =
    li 
      [] 
      [ text (
          String.fromInt age
          ++ " ans"
        )
      ]

Map challenge 2

Map challenge 2

Custom types

Records to store multiple data
(name, score, city....)

Custom types

Records to store multiple data
(name, score, city....)

What about data following various patterns ?

Modeling a shape

      Point                          Circle Float           Rectangle Float Float

Radius

Length

Width

What is the type of a shape?

Reminder: counter

type Msg
    = Increment
    | Decrement

Reminder: counter

type Msg
    = Increment
    | Decrement

First variant

 Second variant

Modeling a shape

      Point                          Circle Float           Rectangle Float Float

Radius

Length

Width

Modeling a shape

      Point                          Circle Float           Rectangle Float Float

Modeling a shape

type Shape =
    Point | Circle Float | Rectangle Float Float

Use a custom type

myRect = Rectangle 10 20

bigCircle = Circle 420000

describe : ???
describe shape =
  case shape of
    Point ->
      "A lonely point"
    Circle radius ->
      "A circle of radius " 
         ++ String.fromFloat radius
    Rectangle width height ->
      "A rectangle of area "
         ++ String.fromFloat (width*height)

Use a custom type

myRect = Rectangle 10 20

bigCircle = Circle 420000

describe : Shape -> String
describe shape =
  case shape of
    Point ->
      "A lonely point"
    Circle radius ->
      "A circle of radius " 
         ++ String.fromFloat radius
    Rectangle width height ->
      "A rectangle of area "
         ++ String.fromFloat (width*height)

Maybe

type Maybe a
    = Nothing
    | Just a

Can be any type

Maybe

type Maybe a
    = Nothing
    | Just a




String.toInt : String -> Maybe Int

 "a" is replaced by "Int"

Maybe

String.toInt : String -> Maybe Int


String.toInt "42" -- Just 42

String.toInt "H2G2" -- Nothing

Deal with the maybe!


res = case (String.toInt "42") of
    Just x ->
        2 * x

    Nothing ->
        0


Deal with the maybe!

res : ???
res = case (String.toInt "42") of
    Just x ->
        2 * x

    Nothing ->
        0


Int

Int

Deal with the maybe!

res : Int
res = case (String.toInt "42") of
    Just x ->
        2 * x

    Nothing ->
        0


Int

Int

Produce a Maybe


getDay dayNumber = case dayNumber of
    0 -> Just "Monday"

    1 -> Just "Tuesday"

    ...

    6 -> Just "Sunday"

    _ -> Nothing

Produce a Maybe

getDay : ???
getDay dayNumber = case dayNumber of
    0 -> Just "Monday"

    1 -> Just "Tuesday"

    ...

    6 -> Just "Sunday"

    _ -> Nothing

Produce a Maybe

getDay : Int -> Maybe String
getDay dayNumber = case dayNumber of
    0 -> Just "Monday"

    1 -> Just "Tuesday"

    ...

    6 -> Just "Sunday"

    _ -> Nothing

"Maybe" represents failure

Deal nicely with Maybe

Map the maybe!

String.toInt "42" -- Just 42


Maybe.map addFive
    (String.toInt "42") -- Just 47

Map the maybe!

String.toInt "42" -- Just 42


Maybe.map addFive
    (String.toInt "42") -- Just 47


Maybe.map addFive
    (String.toInt "H2G2") -- Nothing

Default the maybe!

Maybe.withDefault : a -> Maybe a -> a

Default the maybe!

Maybe.withDefault : a -> Maybe a -> a

Default value

"Just ..." or "Nothing"

Returned value

Default the maybe!

Maybe.withDefault : a -> Maybe a -> a


Maybe.withDefault 0
    (String.toInt "42") -- 42


Maybe.withDefault 0
    (String.toInt "H2G2") -- 0

Pipe operator

Maybe.withDefault 0
    (String.toInt "42")

-- Equivalent :

String.toInt "42"
    |> Maybe.withDefault 0

Pipe operator

Maybe.withDefault 0
    (Maybe.map addFive
        (String.toInt "42")
    )

Pipe operator

Maybe.withDefault 0
    (Maybe.map addFive
        (String.toInt "42")
    )

-- Equivalent :

String.toInt "42"
    |> Maybe.map addFive
    |> Maybe.withDefault 0

Partial application

Maybe.withDefault 0
    (Maybe.map addFive
        (String.toInt "42")
    )

-- Equivalent :

String.toInt "42"
    |> Maybe.map addFive
    |> Maybe.withDefault 0

Wrong number of arguments!

Partial application

add : number -> number -> number
add a b = a + b



addFive : ???
addFive = add 5

Partial application

add : number -> number -> number
add a b = a + b



addFive : ???
addFive = add 5

Partial application

add : number -> number -> number
add a b = a + b



addFive : ???
addFive = add 5

Partial application

add : number -> number -> number
add a b = a + b



addFive : number -> number
addFive = add 5

Partial application

Maybe.map :
    (a -> b)    
    -> Maybe a  
    -> Maybe b  

Partial application

Maybe.map :
    (a -> b)    -- fn to apply
    -> Maybe a  -- original maybe 
    -> Maybe b  -- returned maybe

Partial application

Maybe.map :
    (a -> b)    -- fn to apply
    -> Maybe a  -- original maybe 
    -> Maybe b  -- returned maybe


Maybe.map addFive : ???

Partial application

Maybe.map :
    (a -> b)    -- fn to apply
    -> Maybe a  -- original maybe 
    -> Maybe b  -- returned maybe


Maybe.map addFive : ???

Partial application

Maybe.map :
    (a -> b)    -- fn to apply
    -> Maybe a  -- original maybe 
    -> Maybe b  -- returned maybe


Maybe.map addFive : Maybe a 
                    -> Maybe b

Partial application

Maybe.map :
    (a -> b)    -- fn to apply
    -> Maybe a  -- original maybe 
    -> Maybe b  -- returned maybe


Maybe.map addFive : Maybe Int 
                    -> Maybe Int
                    
addFive : Int -> Int

Pipe operator

Maybe.withDefault 0
    (Maybe.map addFive
        (String.toInt "42")
    )

-- Equivalent :

String.toInt "42"
    |> Maybe.map addFive
    |> Maybe.withDefault 0

Pipe operator

Maybe.withDefault 0
    (Maybe.map addFive
        (String.toInt "42")
    )

-- Equivalent :

String.toInt "42"
    |> Maybe.map addFive
    |> Maybe.withDefault 0

Maybe Int

Pipe operator

Maybe.withDefault 0
    (Maybe.map addFive
        (String.toInt "42")
    )

-- Equivalent :

String.toInt "42"
    |> Maybe.map addFive
    |> Maybe.withDefault 0

Maybe Int

Maybe Int -> Maybe Int

Pipe operator

Maybe.withDefault 0
    (Maybe.map addFive
        (String.toInt "42")
    )

-- Equivalent :

String.toInt "42"
    |> Maybe.map addFive
    |> Maybe.withDefault 0

Maybe Int

Maybe Int -> Maybe Int

Maybe Int -> Int

Introduction to Elm - TWTS 02

By sebbes

Introduction to Elm - TWTS 02

  • 1,014