My wife's great tip on how I should open my presentation is to tell you more about
myself, because according to her, it helps create interest
in the presentation, which again according to her, is way too technical,
and super boring, and she hardly thinks anyone will come, let alone
enjoy it.
gizra
// @amitaibu
Elm
A different approach to frontend webapps
Counter.elm
module Counter where
import Html exposing (..)
import Html.Events exposing (onClick)
-- MODEL
type alias Model = Int
init : Int -> Model
init count = count
-- UPDATE
type Action = Increment | Decrement
update : Action -> Model -> Model
update action model =
case action of
Increment -> model + 1
Decrement -> model - 1
-- VIEW
view : Signal.Address Action -> Model -> Html
view address model =
div []
[ button [ onClick address Decrement ] [ text "-" ]
, div [] [ text (toString model) ]
, button [ onClick address Increment ] [ text "+" ]
]
Elm Architecture
Principle 1: Single source of truth
The state of your whole application is stored in a record tree
Principle 2: State is read-only
The only way to mutate the state is to emit an action describing what happened
module Person where
import Effects exposing (Effects)
import Html exposing (button, div, pre, text, Html)
import Html.Events exposing (onClick)
-- MODEL
type alias Model =
{ age : Int
, kids : Int
, name : String
}
initialModel : Model
initialModel =
{ age = 38
, kids = 3
, name = "Amitai"
}
init : (Model, Effects Action)
init =
( initialModel
, Effects.none
)
-- UPDATE
type Action = Decrement | Increment
update : Action -> Model -> (Model, Effects Action)
update action model =
case action of
Decrement ->
( { model | kids = model.kids - 1 }
, Effects.none
)
Increment ->
( { model | kids = model.kids + 1 }
, Effects.none
)
view : Signal.Address Action -> Model -> Html
view address model =
div []
[ div [] [ text <| "Name: " ++ model.name ]
, div [] [ text <| "Age: " ++ (toString model.age) ]
, div [] [ text <| "Kids num: " ++ (toString model.kids) ]
, button [ onClick address Decrement ] [ text "-" ]
, button [ onClick address Increment ] [ text "+" ]
, pre [] [ text (toString model) ]
]
--
view : Signal.Address Action -> Model -> Html
view address model =
div []
[ viewName model.name
, viewAge model.age
, viewKids model.age
, button [ onClick address Decrement ] [ text "-" ]
, button [ onClick address Increment ] [ text "+" ]
, pre [] [ text (toString model) ]
]
viewName : String -> Html
viewName name =
div [] [ text <| "Name: " ++ name ]
viewAge : Int -> Html
viewAge age =
div [] [ text <| "Age: " ++ (toString age) ]
viewKids : Int -> Html
viewKids kids =
div [] [ text <| "Kids num: " ++ (toString kids) ]
Compile Error Vs Runtime Mistakes
Types 101
type Bool = False | True
type Vehicle = Boat | Plane | Car Int
type alias Model =
{ name : String
, vehicle : Vehicle
}
Type Safety
--
type alias Model =
{ age : Int
, kids : Int
, name : String
}
initialModel : Model
initialModel =
{ age = 38
, kids = 3
, name = "Amitai"
}
type Kids = Kids Int
type alias Model =
{ age : Int
, kids : Kids
, name : String
}
initialModel : Model
initialModel =
{ age = 38
, kids = Kids 3
, name = "Amitai"
}
viewKids : Int -> Html
viewKids kids =
div [] [ text <| "Kids num: " ++ (toString kids) ]
--
viewKids : Kids -> Html
viewKids kids =
let
(Kids val) = kids
in
div [] [ text <| "Kids num: " ++ (toString val) ]
viewKids : Kids -> Html
viewKids (Kids kids) =
div [] [ text <| "Kids num: " ++ (toString val) ]
--
update : Action -> Model -> (Model, Effects Action)
update action model =
case action of
Decrement ->
( { model | kids = model.kids - 1 }
, Effects.none
)
Increment ->
( { model | kids = model.kids + 1 }
, Effects.none
)
--
update : Action -> Model -> (Model, Effects Action)
update action model =
case action of
Decrement ->
let
(Kids val) = model.kids
kids' = Kids (val - 1)
in
( { model | kids = kids' }
, Effects.none
)
Increment ->
let
(Kids val) = model.kids
kids' = Kids (val + 1)
in
( { model | kids = kids' }
, Effects.none
)
40 seconds of my life
Put a limit on Kids
update : Action -> Model -> (Model, Effects Action)
update action model =
case action of
Decrement ->
let
(Kids val) = model.kids
kids' = Kids (val - 1)
in
( { model | kids = kids' }
, Effects.none
)
Increment ->
let
(Kids val) = model.kids
kids' = Kids (val + 1)
in
( { model | kids = kids' }
, Effects.none
)
update : Action -> Model -> (Model, Effects Action)
update action model =
case action of
Decrement ->
let
(Kids val) = model.kids
kids' = if val < 1 then Kids 0 else Kids (val - 1)
in
( { model | kids = kids' }
, Effects.none
)
Increment ->
let
(Kids val) = model.kids
kids' = if val > 4 then Kids 5 else Kids (val + 1)
in
( { model | kids = kids' }
, Effects.none
)
Buisness Logic requires Testing
Unit tests
Pure Functions & Side Effects
-- MODEL
type alias Model = Int
-- UPDATE
type Action
= GetDataFromServer
| UpdateDataFromServer Result
GetDataFromServer ->
( model, Http.get "https://example.com/api/data" |> Task.map UpdateDataFromServer )
UpdateDataFromServer result ->
case result of
Ok val ->
( { model = val } , Effects.none )
Err msg ->
( model , Effects.none )