diff --git a/elm-package.json b/elm-package.json index 994356f..01b7716 100644 --- a/elm-package.json +++ b/elm-package.json @@ -11,7 +11,7 @@ "Parts" ], "dependencies": { - "elm-lang/core": "4.0.0 <= v < 5.0.0" + "elm-lang/core": "5.0.0 <= v < 6.0.0" }, - "elm-version": "0.17.0 <= v < 0.18.0" + "elm-version": "0.18.0 <= v < 0.19.0" } diff --git a/examples/0-counter-part.elm b/examples/0-counter-part.elm index 33fb2da..4b19e4a 100644 --- a/examples/0-counter-part.elm +++ b/examples/0-counter-part.elm @@ -1,8 +1,9 @@ +module Main exposing (..) + import Html exposing (Html, button, div, text) import Html.App as App import Html.Events exposing (onClick) -import Dict - +import Dict import Counter import Parts @@ -17,6 +18,7 @@ main = } + -- MODEL @@ -25,13 +27,14 @@ type alias Model = } -init : (Model, Cmd Msg) +init : ( Model, Cmd Msg ) init = ( { counter = Dict.empty } , Cmd.none ) + -- UPDATE @@ -40,14 +43,15 @@ type Msg | CounterMsg (Parts.Msg Model Msg) -update : Msg -> Model -> (Model, Cmd Msg) +update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of Reset -> init - CounterMsg msg' -> - Parts.update msg' model + CounterMsg msg_ -> + Parts.update msg_ model + -- VIEW @@ -57,6 +61,6 @@ view : Model -> Html Msg view model = div [] - [ Counter.render CounterMsg [0] model + [ Counter.render CounterMsg [ 0 ] model , button [ onClick Reset ] [ text "RESET" ] ] diff --git a/examples/0-counter.elm b/examples/0-counter.elm index 3404027..e977e65 100644 --- a/examples/0-counter.elm +++ b/examples/0-counter.elm @@ -1,3 +1,4 @@ +module Main exposing (..) import Counter import Html exposing (Html, button, div, text) @@ -8,7 +9,7 @@ import Html.Events exposing (onClick) main : Program Never main = App.program - { init = init 0 + { init = init 0 , update = update , view = view , subscriptions = always Sub.none @@ -24,10 +25,10 @@ type alias Model = } -init : Int -> (Model, Cmd Msg) +init : Int -> ( Model, Cmd Msg ) init x = ( { counter = Counter.init x } - , Cmd.none + , Cmd.none ) @@ -40,21 +41,23 @@ type Msg | CounterMsg Counter.Msg -update : Msg -> Model -> (Model, Cmd Msg) +update : Msg -> Model -> ( Model, Cmd Msg ) update message model = case message of Reset -> - init 0 + init 0 CounterMsg msg -> - let - (counter', cmd) = - Counter.update msg model.counter + let + ( counter_, cmd ) = + Counter.update msg model.counter in - ( { model | counter = counter' } + ( { model | counter = counter_ } , Cmd.map CounterMsg cmd ) + + -- VIEW diff --git a/examples/1-counter-pair-part.elm b/examples/1-counter-pair-part.elm index 2434a5e..a27079d 100644 --- a/examples/1-counter-pair-part.elm +++ b/examples/1-counter-pair-part.elm @@ -1,8 +1,9 @@ +module Main exposing (..) + import Html exposing (Html, button, div, text) import Html.App as App import Html.Events exposing (onClick) -import Dict - +import Dict import Counter import Parts @@ -17,6 +18,7 @@ main = } + -- MODEL @@ -25,13 +27,14 @@ type alias Model = } -init : (Model, Cmd Msg) +init : ( Model, Cmd Msg ) init = ( { counter = Dict.empty } , Cmd.none ) + -- UPDATE @@ -40,14 +43,15 @@ type Msg | CounterMsg (Parts.Msg Model Msg) -update : Msg -> Model -> (Model, Cmd Msg) +update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of Reset -> init - CounterMsg msg' -> - Parts.update msg' model + CounterMsg msg_ -> + Parts.update msg_ model + -- VIEW @@ -57,7 +61,7 @@ view : Model -> Html Msg view model = div [] - [ Counter.render CounterMsg [0] model - , Counter.render CounterMsg [1] model + [ Counter.render CounterMsg [ 0 ] model + , Counter.render CounterMsg [ 1 ] model , button [ onClick Reset ] [ text "RESET" ] ] diff --git a/examples/1-counter-pair.elm b/examples/1-counter-pair.elm index 846b031..d5ea1f8 100644 --- a/examples/1-counter-pair.elm +++ b/examples/1-counter-pair.elm @@ -1,3 +1,4 @@ +module Main exposing (..) import Counter import Html exposing (Html, button, div, text) @@ -8,7 +9,7 @@ import Html.Events exposing (onClick) main : Program Never main = App.program - { init = init 0 0 + { init = init 0 0 , update = update , view = view , subscriptions = always Sub.none @@ -25,12 +26,12 @@ type alias Model = } -init : Int -> Int -> (Model, Cmd Msg) +init : Int -> Int -> ( Model, Cmd Msg ) init top bottom = ( { topCounter = Counter.init top , bottomCounter = Counter.init bottom } - , Cmd.none + , Cmd.none ) @@ -44,31 +45,32 @@ type Msg | Bottom Counter.Msg -update : Msg -> Model -> (Model, Cmd Msg) +update : Msg -> Model -> ( Model, Cmd Msg ) update message model = case message of Reset -> init 0 0 Top msg -> - let - (counter', cmd) = - Counter.update msg model.topCounter + let + ( counter_, cmd ) = + Counter.update msg model.topCounter in - ( { model | topCounter = counter' } + ( { model | topCounter = counter_ } , Cmd.map Top cmd ) Bottom msg -> - let - (counter', cmd) = - Counter.update msg model.bottomCounter + let + ( counter_, cmd ) = + Counter.update msg model.bottomCounter in - ( { model | bottomCounter = counter' } + ( { model | bottomCounter = counter_ } , Cmd.map Bottom cmd ) + -- VIEW diff --git a/examples/2-counter-list-part.elm b/examples/2-counter-list-part.elm index a9ca591..3cc76a6 100644 --- a/examples/2-counter-list-part.elm +++ b/examples/2-counter-list-part.elm @@ -1,8 +1,9 @@ +module Main exposing (..) + import Html exposing (Html, button, div, text) import Html.App as App import Html.Events exposing (onClick) -import Dict - +import Dict import Counter import Parts @@ -10,13 +11,14 @@ import Parts main : Program Never main = App.program - { init = (init, Cmd.none) + { init = ( init, Cmd.none ) , subscriptions = always Sub.none , update = update , view = view } + -- MODEL @@ -35,6 +37,7 @@ init = } + -- UPDATE @@ -45,25 +48,26 @@ type Msg reset : Int -> Model -> Model -reset k model = - .reset (Counter.find [k]) model +reset k model = + .reset (Counter.find [ k ]) model -update : Msg -> Model -> (Model, Cmd Msg) +update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of - Insert -> + Insert -> ( { model | last = model.last + 1 } |> reset (model.last + 1) , Cmd.none ) - Remove -> + Remove -> ( { model | first = model.first + 1 } |> reset model.first , Cmd.none ) - CounterMsg msg' -> - Parts.update msg' model + CounterMsg msg_ -> + Parts.update msg_ model + -- VIEW @@ -71,7 +75,7 @@ update msg model = view : Model -> Html Msg view model = - let + let remove = button [ onClick Remove ] [ text "Remove" ] @@ -79,8 +83,8 @@ view model = button [ onClick Insert ] [ text "Add" ] counters = - [model.first .. model.last] - |> List.map - (\idx -> Counter.render CounterMsg [idx] model) + List.range model.first model.last + |> List.map + (\idx -> Counter.render CounterMsg [ idx ] model) in - div [] ([remove, insert] ++ counters) + div [] ([ remove, insert ] ++ counters) diff --git a/examples/2-counter-list.elm b/examples/2-counter-list.elm index df8f575..c222827 100644 --- a/examples/2-counter-list.elm +++ b/examples/2-counter-list.elm @@ -1,10 +1,11 @@ +module Main exposing (..) + import Counter import Html exposing (..) import Html.App as App import Html.Events exposing (..) - main : Program Never main = App.beginnerProgram @@ -19,12 +20,13 @@ main = type alias Model = - { counters : List ( ID, Counter.Model ) - , nextID : ID - } + { counters : List ( ID, Counter.Model ) + , nextID : ID + } -type alias ID = Int +type alias ID = + Int init : Model @@ -62,12 +64,11 @@ update msg model = Modify id counterMsg -> let - updateCounter (counterID, counterModel) = + updateCounter ( counterID, counterModel ) = if counterID == id then - (counterID, Counter.update counterMsg counterModel |> fst) - + ( counterID, Counter.update counterMsg counterModel |> Tuple.first ) else - (counterID, counterModel) + ( counterID, counterModel ) in { model | counters = List.map updateCounter model.counters } @@ -88,9 +89,9 @@ view model = counters = List.map viewCounter model.counters in - div [] ([remove, insert] ++ counters) + div [] ([ remove, insert ] ++ counters) -viewCounter : (ID, Counter.Model) -> Html Msg -viewCounter (id, model) = +viewCounter : ( ID, Counter.Model ) -> Html Msg +viewCounter ( id, model ) = Counter.view (Modify id) model diff --git a/examples/Counter.elm b/examples/Counter.elm index 39058eb..eb5aae6 100644 --- a/examples/Counter.elm +++ b/examples/Counter.elm @@ -3,15 +3,14 @@ module Counter exposing (Model, init, Msg, update, view, render, find, Index, In import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (onClick) - import Parts -- MODEL -type alias Model - = Int +type alias Model = + Int init : Int -> Model @@ -19,6 +18,7 @@ init count = count + -- UPDATE @@ -27,14 +27,15 @@ type Msg | Decrement -update : Msg -> Model -> (Model, Cmd Msg) +update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of Increment -> - (model + 1, Cmd.none) + ( model + 1, Cmd.none ) Decrement -> - (model - 1, Cmd.none) + ( model - 1, Cmd.none ) + -- VIEW @@ -42,46 +43,51 @@ update msg model = view : (Msg -> m) -> Model -> Html m view lift model = - div + div [] - [ button [ onClick (lift Decrement) ] [ text "-" ] + [ button [ onClick (lift Decrement) ] [ text "-" ] , div [ countStyle ] [ text (toString model) ] - , button [ onClick (lift Increment) ] [ text "+" ] + , button [ onClick (lift Increment) ] [ text "+" ] ] countStyle : Attribute msg countStyle = style - [ ("font-size", "20px") - , ("font-family", "monospace") - , ("display", "inline-block") - , ("width", "50px") - , ("text-align", "center") + [ ( "font-size", "20px" ) + , ( "font-family", "monospace" ) + , ( "display", "inline-block" ) + , ( "width", "50px" ) + , ( "text-align", "center" ) ] + -- PART + + type alias Indexed m = Parts.Indexed (List Int) m + type alias Index = Parts.Index (List Int) -type alias Container c = + +type alias Container c = { c | counter : Indexed Model } -set : Parts.Set (Indexed Model) (Container c) -set x y = +set : Parts.Set (Indexed Model) (Container c) +set x y = { y | counter = x } render : (Parts.Msg (Container c) m -> m) -> Index -> Container c -> Html m -render = +render = Parts.create view (Parts.generalize update) .counter set (init 0) find : Index -> Parts.Accessors Model (Container c) -find = +find = Parts.accessors .counter set (init 0) diff --git a/src/Parts.elm b/src/Parts.elm index 2c9ba46..3ab95bd 100644 --- a/src/Parts.elm +++ b/src/Parts.elm @@ -1,18 +1,33 @@ -module Parts exposing - ( Update, View - , Get, Set, embedView, embedUpdate - , Index, Indexed, indexed - , Msg - , partial, update, update', create, create1, accessors, Accessors - , generalize, pack, pack1 - ) - -{-| +module Parts + exposing + ( Update + , View + , Get + , Set + , embedView + , embedUpdate + , Index + , Indexed + , indexed + , Msg + , partial + , update + , update_ + , create + , create1 + , accessors + , Accessors + , generalize + , pack + , pack1 + ) + +{-| Given a TEA component with model type `model` and message type `msg`, we construct a variant component which knows how to extract its model from a c model `c` and produces generic messages `Msg c`. The consuming component is assumed -to have message type `obs` (for "observation"). +to have message type `obs` (for "observation"). # Lazyness @@ -20,32 +35,32 @@ Recall that `Html.Lazy` avoids re-computing views when the model doesn't change across updates. However, "doesn't change" does not mean `model == model'` but rather the stricter `model === model'` (in Javascript terms). That is, the old and new model must not only be structurally the same, they must be literally the same -data-structure in memory. +data-structure in memory. Parts generally do not achieve referential equality of no-op updates, since we -are wrapping updates conceptually like this: +are wrapping updates conceptually like this: - let (submodel, submsgs) = SubComponent.update msg model.submodel - model' = { model | submodel = submodel } - in - ... + let (submodel, submsgs) = SubComponent.update msg model.submodel + model' = { model | submodel = submodel } + in + ... In the second line, even if `submodel == model.submodel` and so `model == -model'`, we won't have (in Javascript terms) `model === model'`. +model'`, we won't have (in Javascript terms) `model === model'`. For this reason, the result of `update` functions used in parts should be -`Maybe (model, Cmd msg)` rather than the usual `(model, Cmd msg)`; the -`Nothing` case signifies a no-op. +`Maybe (model, Cmd msg)` rather than the usual `(model, Cmd msg)`; the +`Nothing` case signifies a no-op. # Communicating to the parent component Because parts wrap messages in an opaque type, the parent component loses the -ability to inspect and maybe react to messages of the part. We recover this -ability by requiring the `update` function to take as parameter a lifting -function which lifts the parts messages to that of its parent. +ability to inspect and maybe react to messages of the part. We recover this +ability by requiring the `update` function to take as parameter a lifting +function which lifts the parts messages to that of its parent. @docs Update, View -# Model embeddings +# Model embeddings @docs Get, Set, embedView, embedUpdate @docs accessors, Accessors @@ -53,7 +68,7 @@ function which lifts the parts messages to that of its parent. @docs Index, Indexed, indexed # Message embeddings -@docs Msg, update, update', partial +@docs Msg, update, update_, partial # Part construction @docs create, create1, generalize, pack, pack1 @@ -65,62 +80,64 @@ import Dict exposing (Dict) -- TYPES -{-| Update functions. +{-| Update functions. TEA update function with explicit message lifting and no-op. You should have: - fst (update f msg model) == Nothing -- No change to model - fst (update f msg model) == Just model' -- Change to model' + fst (update f msg model) == Nothing -- No change to model + fst (update f msg model) == Just model' -- Change to model' -} -type alias Update model msg obs = - (msg -> obs) -> msg -> model -> (Maybe model, Cmd obs) +type alias Update model msg obs = + (msg -> obs) -> msg -> model -> ( Maybe model, Cmd obs ) -{-| Standard TEA view function type. +{-| Standard TEA view function type. -} -type alias View model a = - model -> a +type alias View model a = + model -> a + -- EMBEDDINGS -{-| Type of "getter": fetch component model `m` from c model `c`. +{-| Type of "getter": fetch component model `m` from c model `c`. -} type alias Get model c = - c -> model + c -> model -{-| Type of "setter": update component model `m` in c `c`. +{-| Type of "setter": update component model `m` in c `c`. -} -type alias Set model c = - model -> c -> c +type alias Set model c = + model -> c -> c -{-| Lift a `view` to one which knows how to retrieve its `model` from -a c model `c`. +{-| Lift a `view` to one which knows how to retrieve its `model` from +a c model `c`. -} embedView : Get model c -> View model a -> View c a -embedView get view = - get >> view +embedView get view = + get >> view -{-| Lift an `Update` from operating on `model` to a c model `c`. +{-| Lift an `Update` from operating on `model` to a c model `c`. -} -embedUpdate : +embedUpdate : Get model c - -> Set model c - -> Update model msg obs - -> Update c msg obs -embedUpdate get set update = - \f msg c -> - update f msg (get c) - |> map1st (Maybe.map <| flip set c) + -> Set model c + -> Update model msg obs + -> Update c msg obs +embedUpdate get set update = + \f msg c -> + update f msg (get c) + |> map1st (Maybe.map <| flip set c) + -- INDEXED EMBEDDINGS - + {-| Type of indices. An index has to be `comparable` For example: @@ -129,28 +146,29 @@ support nested dynamically constructed elements: Use indices `[0]`, `[1]`, ... for statically known top-level components, then use `[0,0]`, `[0,1]`, ... for a dynamically generated list of components. -} -type alias Index comparable = - comparable +type alias Index comparable = + comparable {-| Indexed families of things. -} -type alias Indexed comparable a - = Dict (Index comparable) a +type alias Indexed comparable a = + Dict (Index comparable) a {-| Fix a getter and setter for an `Indexed comparable model` to a particular `Index comparable`. -} -indexed : +indexed : Get (Indexed comparable model) c - -> Set (Indexed comparable model) c - -> model - -> (Index comparable -> Get model c, Index comparable -> Set model c) -indexed get set model0 = - ( \idx c -> Dict.get idx (get c) |> Maybe.withDefault model0 - , \idx model c -> set (Dict.insert idx model (get c)) c - ) - + -> Set (Indexed comparable model) c + -> model + -> ( Index comparable -> Get model c, Index comparable -> Set model c ) +indexed get set model0 = + ( \idx c -> Dict.get idx (get c) |> Maybe.withDefault model0 + , \idx model c -> set (Dict.insert idx model (get c)) c + ) + + -- EMBEDDING MESSAGES @@ -158,33 +176,33 @@ indexed get set model0 = {-| Similar to how embeddings enable collecting models of different type in a single model c, we collect messages in a single "master message" type. Messages exist exclusively to be dispatched by a corresponding -`update` function; we can avoid distinguishing between different types of +`update` function; we can avoid distinguishing between different types of messages by dispatching not the `Msg` itself, but a partially applied update -function `update msg`. +function `update msg`. -It's instructive to compare `Msg` to the type of `update` partially applied to +It's instructive to compare `Msg` to the type of `update` partially applied to an actual carried message `m`: - update : m -> c -> (c, Cmd m) - (update m) : c -> (c, Cmd m) + update : m -> c -> (c, Cmd m) + (update m) : c -> (c, Cmd m) -} -type Msg c obs = - Msg (c -> (Maybe c, Cmd obs)) +type Msg c obs + = Msg (c -> ( Maybe c, Cmd obs )) -{-| Generic update function for `Msg`. +{-| Generic update function for `Msg`. -} -update : Msg c obs -> c -> (c, Cmd obs) -update (Msg f) c = - f c |> map1st (Maybe.withDefault c) - +update : Msg c obs -> c -> ( c, Cmd obs ) +update (Msg f) c = + f c |> map1st (Maybe.withDefault c) + -{-| Generic update function for `Msg`, explicit no-op +{-| Generic update function for `Msg`, explicit no-op -} -update' : Msg c obs -> c -> (Maybe c, Cmd obs) -update' (Msg f) c = - f c - +update_ : Msg c obs -> c -> ( Maybe c, Cmd obs ) +update_ (Msg f) c = + f c + -- PARTS @@ -194,50 +212,51 @@ update' (Msg f) c = a generic Msg. -} partial : (Msg c obs -> obs) -> Update c msg obs -> msg -> Msg c obs -partial fwd upd msg = - Msg (\c -> - upd (partial fwd upd >> fwd) msg c) +partial fwd upd msg = + Msg + (\c -> + upd (partial fwd upd >> fwd) msg c + ) {-| Pack up a an indexed component message `msg` in an `obs`. -} -pack - : Update model msg obs - -> Get (Indexed comparable model) c - -> Set (Indexed comparable model) c - -> model - -> (Msg c obs -> obs) - -> Index comparable - -> msg - -> obs -pack update get0 set0 model0 fwd = - let - (get, set) = - indexed get0 set0 model0 - in - \idx -> - partial fwd (embedUpdate (get idx) (set idx) update) >> fwd +pack : + Update model msg obs + -> Get (Indexed comparable model) c + -> Set (Indexed comparable model) c + -> model + -> (Msg c obs -> obs) + -> Index comparable + -> msg + -> obs +pack update get0 set0 model0 fwd = + let + ( get, set ) = + indexed get0 set0 model0 + in + \idx -> + partial fwd (embedUpdate (get idx) (set idx) update) >> fwd {-| Pack up a singleton component message `msg` in an `obs`. -} -pack1 - : Update model msg obs - -> Get model c - -> Set model c - -> (Msg c obs -> obs) - -> msg - -> obs -pack1 update get set fwd = - partial fwd (embedUpdate get set update) >> fwd - - - -{-| From `update` and `view` functions, produce a `view` function which (a) +pack1 : + Update model msg obs + -> Get model c + -> Set model c + -> (Msg c obs -> obs) + -> msg + -> obs +pack1 update get set fwd = + partial fwd (embedUpdate get set update) >> fwd + + +{-| From `update` and `view` functions, produce a `view` function which (a) fetches its model from a `c` model, and (b) dispatches generic `Msg` -messages. +messages. -Its instructive to compare the types of the input `view` and `update` for a +Its instructive to compare the types of the input `view` and `update` for a typical case. Notice that `create` transforms `model` -> `c` and `Html m` -> `Html obs`. @@ -249,101 +268,100 @@ typical case. Notice that `create` transforms `model` -> `c` and view : Index comparable -> c -> List (Attributes obs) -> List (Html obs) -> Html obs Note that the input `view` function is assumed to take a function lifting its -messages. +messages. -} -create - : ((msg -> obs) -> View model a) - -> Update model msg obs - -> Get (Indexed comparable model) c - -> Set (Indexed comparable model) c - -> model - -> (Msg c obs -> obs) - -> Index comparable - -> View c a -create view update get0 set0 model0 fwd = - let - get = - fst (indexed get0 set0 model0) - - embeddedUpdate = - pack update get0 set0 model0 fwd - in - \idx c -> - (view (embeddedUpdate idx) (get idx c)) +create : + ((msg -> obs) -> View model a) + -> Update model msg obs + -> Get (Indexed comparable model) c + -> Set (Indexed comparable model) c + -> model + -> (Msg c obs -> obs) + -> Index comparable + -> View c a +create view update get0 set0 model0 fwd = + let + get = + Tuple.first (indexed get0 set0 model0) + + embeddedUpdate = + pack update get0 set0 model0 fwd + in + \idx c -> + (view (embeddedUpdate idx) (get idx c)) {-| Like `create`, but for components that are assumed to have only one instance. -} -create1 - : ((msg -> obs) -> View model a) - -> Update model msg obs - -> Get model c - -> Set model c - -> (Msg c obs -> obs) - -> View c a - -create1 view update get set fwd = - let - embeddedUpdate = - partial fwd (embedUpdate get set update) >> fwd - in - embedView get <| view embeddedUpdate - - -{-| For components where consumers do care about the model of the -component, use the `accessors` function below to generate suitable, +create1 : + ((msg -> obs) -> View model a) + -> Update model msg obs + -> Get model c + -> Set model c + -> (Msg c obs -> obs) + -> View c a +create1 view update get set fwd = + let + embeddedUpdate = + partial fwd (embedUpdate get set update) >> fwd + in + embedView get <| view embeddedUpdate + + +{-| For components where consumers do care about the model of the +component, use the `accessors` function below to generate suitable, well, accessors. -} -type alias Accessors model c = - { get : Get model c - , set : Set model c - , map : (model -> model) -> c -> c - , reset : c -> c - } +type alias Accessors model c = + { get : Get model c + , set : Set model c + , map : (model -> model) -> c -> c + , reset : c -> c + } {-| Generate accessors. -} -accessors - : Get (Indexed comparable model) c - -> Set (Indexed comparable model) c - -> model - -> Index comparable - -> Accessors model c - -accessors get0 set0 model0 idx = - let - (get, set) = - indexed get0 set0 model0 - in - { get = get idx - , set = set idx - , map = \f c -> (get idx) c |> f |> flip (set idx) c - , reset = \c -> get0 c |> Dict.remove idx |> (\m -> set0 m c) - } - - -{-| Generalise a standard TEA `update` function to one fitting with -parts (explicit lifter, explicit no-op). +accessors : + Get (Dict comparable model) c + -> Set (Dict comparable model) c + -> model + -> Index comparable + -> Accessors model c +accessors get0 set0 model0 idx = + let + ( get, set ) = + indexed get0 set0 model0 + in + { get = get idx + , set = set idx + , map = \f c -> (get idx) c |> f |> flip (set idx) c + , reset = \c -> get0 c |> Dict.remove idx |> (\m -> set0 m c) + } + + +{-| Generalise a standard TEA `update` function to one fitting with +parts (explicit lifter, explicit no-op). -} -generalize - : (msg -> model -> (model, Cmd msg)) - -> Update model msg obs -generalize upd f m c = - upd m c - |> map1st Just - |> map2nd (Cmd.map f) +generalize : + (msg -> model -> ( model, Cmd msg )) + -> Update model msg obs +generalize upd f m c = + upd m c + |> map1st Just + |> map2nd (Cmd.map f) --- HELPERS - -map1st : (a -> c) -> (a,b) -> (c,b) -map1st f (x,y) = (f x, y) +-- HELPERS -map2nd : (b -> c) -> (a,b) -> (a,c) -map2nd f (x,y) = (x, f y) +map1st : (a -> c) -> ( a, b ) -> ( c, b ) +map1st f ( x, y ) = + ( f x, y ) +map2nd : (b -> c) -> ( a, b ) -> ( a, c ) +map2nd f ( x, y ) = + ( x, f y )