Elm



Elm

0 0


DrupalCon-NOLA


On Github Gizra-Presentations / DrupalCon-NOLA

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.
  • What do you mean?
  • I don't know. Like, tell them your name, your age and how many kids you have
  • Idiot, you are only 37
  • Eventhough biographically incorrect, proceed
gizra // @amitaibu
  • Intro
  • Since beggining of 2016, official US office
  • Please tweet, makes me feel important

Elm

A different approach to frontend webapps
  • Let's understand what it solves
  • Agree or disagree, all JS frameworks have in common use JS
  • If JS ninja ok
  • Pragmatic approach: Quickest, cheapest, solid, and keep devs moral
  • Bash Angular 1. Elm provides better tools to deal
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 "+" ]
    ]
  • Functional programming language. If you know - good for you
  • Functions don't have state and data is immutable
  • It's a language. Similarity to Haskell. Compiled to JS, HTML, CSS (you can use Bootstrap/ Semantic UI)

Elm Architecture

  • Set of best practices
  • How to structure your application
  • How to connect components
  • Model -> Update -> View
  • Consists of a few prinicpels

Principle 1: Single source of truth

The state of your whole application is stored in a record tree
  • Saw first time it resonated
  • Angular 1 - where is your state? Router. Services.
  • Async nature of JS
  • Condition of your webapp
  • In case of bugs, hard to reproduce
  • Maybe counter intiutive (seperation of concerns) - control webapp

Principle 2: State is read-only

The only way to mutate the state is to emit an action describing what happened
  • It's 2016. The root of all evil in this worlds. heart worming, positive, non-cynical, world - 2 way data binding
  • Angular 1 site - Save me so many jQuery lines
  • What made us love it, is what we now hate
  • If view changes model, chain of actions. Can't control state
  • Back to our award winning webapp
  • It's a glorified counter example
  • How to think elm
  • Learning curve, but things just make sense
						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
      )
					
  • Boiler code
  • Show the essence
  • When I click the button nothing will happen
  • Update is the brain of webapp, here we have logic
  • Next: View
						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) ]
    ]





--
					
  • Not HTML, but still quite convoluted
  • Next: Clenup the View
						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) ]
					

Everything in Elm is a function

  • Oh no! how embarrasing! (show code)
  • Kids is wrong
  • Debug shows model is right

Compile Error Vs Runtime Mistakes

  • No Runtime errors in Elm
  • Shift as much runtime mistakes to compile time error

Types 101

						type Bool = False | True
					
						type Vehicle = Boat | Plane | Car Int

type alias Model =
  { name    : String
  , vehicle : Vehicle
  }
					
  • So rich
  • Multiple cars
  • Int wrapped with a Car type

Type Safety

  • Have types in place, helping the compiler in helping us
  • Preventing mistakes
						--

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) ]


--
					

View before change

						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) ]


--
					
Next: Kids in update func
						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
      )




--
					
  • Some code needs to change
  • Kids is no longer holding just an Int
						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 - The only place for Business logic and change our data
  • more complicated requiremenets, to put it in context so it won't sound to harsh

40 seconds of my life

  • Try to notice the sound of the piano in the end, that's one of my favorite parts
  • I think it's evident what the requiremenet is

Put a limit on Kids

  • Aya - the name of the crying girl, was throwing a fit over the fact the yellow potato head glasses didn't fit her better
  • Can't go below zero, can go beyond a certain number
  • How would you do on Angular1?
  • Disable html HTML?
  • Subscribe to model changing and undo it?
  • Unidirectional, we don't have 2 way binding problems
						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
        )
					
  • Before change
						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

  • Who writes unit tests in JS?
  • Who enjoys it?
  • For the viewers at home, very few raised there hands, can't blame them
  • Probably the same amount of joy and happiness those guys in the back of Kim Jung Un must be feeling.
  • Probably the same amount of joy and happiness those guys in the back of Kim Jung Un must be feeling.

Unit tests

  • What makes unit tests easy Elm
  • Time to talk about "Pure functions and side effects"

Pure Functions & Side Effects

  • Elm is a black box with no connection to outside world
-- MODEL
 type alias Model = Int

 -- UPDATE
 type Action
   = GetDataFromServer
   | UpdateDataFromServer Result
  • Simple counter example, data from server
  • Action - nothing happens by itself
  • Code not compiled, for simplicity and brevity
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 )
  • Encourages us to be better developers

Headless Drupal

  • Users, permissions, content modeling, entity reference, RESTful

  • next: Timewatch

  • Office timewatch
https://github.com/amitaibu/elm-hedley
  • What helps us decide to transition from Angular, jump over react to Elm.
  • Typical webapp, login, github
  • Router
  • Maps
  • User interactions to filter events
  • Uploading files with drag and drop
  • This can be your own Drupal site
  • Can by hybrid

Yeoman Generator

https://github.com/Gizra/generator-elmlang
  • Gulp
  • browserSync
  • Auto-compile
  • Sass
  • Bundle and Deploy to gh-pages
  • Structured app
  • A few tests
  • Travis
  • Read more one elm-lang and our blog posts
What about NodeJs?
My functional programming journey
  • I started with Elm
  • Looked at Symfony as holy grail (decoupled, composition)
  • Objects immutable, might have state
  • For more complex sites with have Drupal and NodeJs as proxy server
gizra | We now have a US office. Seriously.
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. What do you mean? I don't know. Like, tell them your name, your age and how many kids you have