An Introduction to Elm Series: Solution to ‘Pair of Counters’ example


In http://guide.elm-lang.org/architecture/modularity/counter_pair.html, we are given an application that reuses the counter application that was converted into a module for the purpose of this tutorial.

We were asked to make a swap between the two instances of the counter. That was easy due to the nature of the updates that create a new version of the model each time instead of updating it. So, we just swapped the instances of the two counters in the update.

Later, we were asked to extend the counter to log some statistics, since the parent module was not using the model directly it was very easy for us to extend the model and implement the changes needed in the update and the view of the model. No signatures were changed and init was modified to initialize all statistics along with the value.

Parent Module (1-counter-pair.elm)

import Counter
import Html exposing (Html, button, div, text)
import Html.App as App
import Html.Events exposing (onClick)



main =
  App.beginnerProgram
    { model = init 0 0
    , update = update
    , view = view
    }



-- MODEL


type alias Model =
  { topCounter : Counter.Model
  , bottomCounter : Counter.Model
  }


init : Int -> Int -> Model
init top bottom =
  { topCounter = Counter.init top
  , bottomCounter = Counter.init bottom
  }



-- UPDATE


type Msg
  = Reset
  | Top Counter.Msg
  | Bottom Counter.Msg
  | Swap


update : Msg -> Model -> Model
update message model =
  case message of
    Reset ->
      init 0 0

    Top msg ->
      { model | topCounter = Counter.update msg model.topCounter }

    Bottom msg ->
      { model | bottomCounter = Counter.update msg model.bottomCounter }

    Swap ->
      { model | bottomCounter = model.topCounter, topCounter = model.bottomCounter }


-- VIEW


view : Model -> Html Msg
view model =
  div
    []
    [ App.map Top (Counter.view model.topCounter)
    , App.map Bottom (Counter.view model.bottomCounter)
    , button [ onClick Reset ] [ text "RESET" ]
    , button [ onClick Swap ] [ text "Swap" ]
    ]

Counter Module (Counter.elm)

module Counter exposing (Model, Msg, init, update, view)

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)



-- MODEL


type alias Model =
  { count : Int
  , max' : Int
  , min' : Int
  , increment : Int
  , decrement : Int
  }


init : Int -> Model
init count =
  Model count 0 0 0 0

-- UPDATE


type Msg
  = Increment
  | Decrement


update : Msg -> Model -> Model
update msg model =
  case msg of
    Increment ->
      { model |
        count = model.count + 1
        , max' = Basics.max (model.count + 1) model.max'
        , increment = model.increment + 1
      }

    Decrement ->
      { model |
        count = model.count - 1
        , min' = Basics.min (model.count - 1) model.min'
        , decrement = model.decrement + 1
      }



-- VIEW


view : Model -> Html Msg
view model =
  div []
    [ button [ onClick Decrement ] [ text "-" ]
    , div [ countStyle ] [ text (toString model.count) ]
    , button [ onClick Increment ] [ text "+" ]
    , div [] [ text ("Max " ++ (toString model.max')) ]
    , div [] [ text ("Min " ++ (toString model.min')) ]
    , div [] [ text ("Increment " ++ (toString model.increment)) ]
    , div [] [ text ("Decrement " ++ (toString model.decrement)) ]
    ]


countStyle : Attribute msg
countStyle =
  style
    [ ("font-size", "20px")
    , ("font-family", "monospace")
    , ("display", "inline-block")
    , ("width", "50px")
    , ("text-align", "center")
    ]

 

You can download the solution from here 1-counter-pair (compressed) (124 downloads)

Leave a Reply