Skip to content

Instantly share code, notes, and snippets.

@markhamburg
Created September 9, 2016 21:23
Show Gist options
  • Save markhamburg/b4613175fd65f6d25a58e3f9c0044a09 to your computer and use it in GitHub Desktop.
Save markhamburg/b4613175fd65f6d25a58e3f9c0044a09 to your computer and use it in GitHub Desktop.
Another version of the counter with stack example for private state, this time with a utility to encapsulate the repeated update logic.
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.App
import Html.Events exposing (..)
type CounterMsg
= Decrement
| Increment
| Push
| Pop
type CounterPrivate =
Private (List Int)
-- Initialize the private data for a counter
initCounterPrivate : Int -> CounterPrivate
initCounterPrivate value =
Private []
-- Update the private data for a counter given a change in
-- the public value
updateCounterPrivate : Int -> CounterPrivate -> CounterPrivate
updateCounterPrivate value old =
old
-- Generate a view for a counter with value stack
viewCounter : Int -> CounterPrivate -> Html CounterMsg
viewCounter value (Private stack) =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (toString value) ]
, button [ onClick Increment ] [ text "+" ]
, button [ onClick Push ] [ text "Push" ]
, button [ onClick Pop, disabled (stack == []) ] [ text "Pop" ]
]
-- Update for a counter with stack
updateCounter : CounterMsg -> Int -> CounterPrivate -> (Int, CounterPrivate)
updateCounter msg value ((Private stack) as private) =
case msg of
Decrement ->
(value - 1, private)
Increment ->
(value + 1, private)
Push ->
(value, Private <| value :: stack)
Pop ->
case stack of
[] -> (value, private)
x :: rest -> (x, Private rest)
-- Our model will have two view with private stacks working with
-- the same counter
type alias Model =
{ value : Int
, privateA : CounterPrivate
, privateB : CounterPrivate
}
type Msg
= ToA CounterMsg
| ToB CounterMsg
init : Int -> Model
init value =
{ value = value
, privateA = initCounterPrivate value
, privateB = initCounterPrivate value
}
view : Model -> Html Msg
view model =
div []
[ Html.App.map ToA <| viewCounter model.value model.privateA
, Html.App.map ToB <| viewCounter model.value model.privateB
]
update : Msg -> Model -> Model
update msg model =
case msg of
ToA cmsg ->
updatePublicPrivate
(.value, setValue)
(.privateA, (\private model -> { model | privateA = private }))
updateCounter
cmsg
model
ToB cmsg ->
updatePublicPrivate
(.value, setValue)
(.privateB, (\private model -> { model | privateB = private }))
updateCounter
cmsg
model
type alias GetterSetter container value =
((container -> value), (value -> container -> container))
updatePublicPrivate :
GetterSetter model public
-> GetterSetter model private
-> (msg -> public -> private -> (public, private))
-> msg
-> model
-> model
updatePublicPrivate
(getPublic, setPublic) (getPrivate, setPrivate) update msg model =
let
(newPublic, newPrivate) =
update msg (getPublic model) (getPrivate model)
in
model |> setPublic newPublic |> setPrivate newPrivate
-- When we update the shared counter value, we provide an opportunity
-- for the private states to update as well.
setValue : Int -> Model -> Model
setValue newValue model =
if newValue == model.value then
model
else
{ model
| value = newValue
, privateA = updateCounterPrivate newValue model.privateA
, privateB = updateCounterPrivate newValue model.privateB
}
-- Wire it up and run it
main =
Html.App.beginnerProgram { model = init 0, view = view, update = update }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment