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
Starting point: https://ellie-app.com/6qJZr8QBmvWa1
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
complete solution :
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