And if we were more deeply nested, the NeedAuthorization could also have
been forwarded via code like this — note also that I fixed the failure to
properly type the case entries in my previous message:

applySubModelOutMsg : SubModelOutMsg -> Model -> ( Model, List OutMsg )
applySubModelOutMsg subModelOutMsg model =
    case subModelOutMsg of
        SubModelCommand cmd ->
            ( model, [ Command <| Cmd.map ToSubModel cmd ] )
        SubModelNeedAuthorization updateGen ->
            ( model, [ NeedAuthorization (updateGen >> updateSubModel) ] )


Mark


On Tue, Mar 21, 2017 at 11:26 AM, Mark Hamburg <[email protected]>
wrote:

> Oliver asked whether our out messages approach had an equivalent of
> Cmd.map. I'll paraphrase some code to show how it works. It's definitely
> more code than the simple Cmd approach but it provides more functionality
> as well. I'm simplifying away a lot of details like the fact that the login
> submodel below would probably be stored as a `Maybe Login.Model` since once
> we've logged in we don't need it any more.
>
> update : Msg -> Model -> ( Model, List OutMsg )
> update msg model =
>     case msg of
>         ToLogin loginMsg ->
>             updateLogin (Login.update loginMsg) model
>
> updateLogin :
>     (Login.Model -> ( Login.Model, List Login.OutMsg ))
>     -> (Model -> ( Model, List OutMsg ))
> updateLogin loginUpdater model =
>     Update.start model.login
>         |> Update.andThen loginUpdater
>         |> Update.map (asLoginIn model)
>         |> Update.applyOutMessages applyLoginOutMsg
>
> asLoginIn : Model -> Login.Model -> Model
> asLoginIn model newLogin =
>     { model | login = newLogin }
>
> applyLoginOutMsg : Login.OutMsg -> Model -> ( Model, List OutMsg )
> applyLoginOutMsg loginOutMsg model =
>     case loginOutMsg of
>         Login.Command cmd ->
>             ( model, [ Command <| Cmd.map ToLogin cmd ] )
>         Login.DidLogin authorization ->
>             didLogin authorization model
>
> didLogin : Authorization -> Model -> ( Model, List OutMsg )
> didLogin authorization model = ...
>
> The Update module provides a collection of functions for dealing with
> types with the shape ( Model, List OutMsg ). Update.map maps the model.
> Update.andThen is a monadic chaining operation. Update.applyOutMessages
> folds the out messages over the model performing a series of updates and
> accumulating a new list of out messages (generally of a different type).
>
> Applying out messages is definitely more involved than mapping commands
> and it gets repetitive to always have to handle standard cases like mapping
> commands, but the flexibility to have the submodel signal something like
> the fact that we successfully logged in is a big win and standardizing on
> doing this via out messages builds a more consistent code rhythm than
> adding lots of one off extra results to update functions.
>
> Mark
>
> P.S. If anyone has a recommendation for a better name for
> Update.applyOutMessages, I'd love to hear it. The current name feels
> excessively verbose.
>
> P.P.S. If you want your mind more deeply twisted, here is what we do when
> we want to store the auth token fairly high up but a piece of code needs it
> for constructing an HTTP request:
>
> type SubModelOutMsg
>     = Command (Cmd Msg)
>      | NeedAuthorization (Authorization -> SubModel -> ( SubModel, List
> SubModelOutMsg ))
>
> applySubModelOutMsg : SubModelOutMsg -> Model -> ( Model, List OutMsg )
> applySubModelOutMsg subModelOutMsg model =
>     case subModelOutMsg of
>         Command cmd ->
>             ( model, [ Command <| Cmd.map ToSubModel cmd ] )
>         NeedAuthorization updateGen ->
>             updateSubModel (updateGen model.authorization) model
>
> updateSubModel :
>     (SubModel -> ( SubModel, List SubModelOutMsg ) )
>     -> (Model -> ( Model, List OutMsg ))
> updateSubModel subModelUpdater model =
>     Update.start model.subModel
>         |> Update.andThen subModelUpdater
>         |> Update.map (asSubModelIn model)
>         |> Update.applyOutMessages applySubModelOutMsg
>
>
>
> On Tue, Mar 21, 2017 at 9:11 AM, 'Rupert Smith' via Elm Discuss <
> [email protected]> wrote:
>
>> On Monday, March 20, 2017 at 11:58:38 AM UTC, Eirik Sletteberg wrote:
>>>
>>> In larger Elm apps, it makes sense to divide Updaters so you can
>>> package-by-feature.
>>> For example, a single page application could have updaters like this:
>>>
>>> - Configuration updater
>>> - Session updater
>>> - User Profile updater
>>> - User Settings updater
>>> - Content updater
>>> - Some other business specific updater
>>>
>>> The challenge is when there are dependencies between Updaters, for
>>> example the User Profile model might need data from the Session model, the
>>> Session updater might need to send messages to the User updater (Load user
>>> profile when session is updated), or the Content updater may need to check
>>> for the Session updater (get session ID to send as parameter to the API
>>> when fetching content), or some business-specific updater may need to
>>> interact with both the Content updater, the User updater, and the
>>> Configuration updater.
>>>
>>
>> I would apply a technique similar to CRC Cards from OO design. Instead of
>> Class-Responsibility-Collaboration, make it
>> Module-Responsibility-Collaborations.
>>
>> You have got a fair idea of what the split of responsibilities into
>> modules is (configuration, session, user profile, user setting, content,
>> and so on). For each module it is worth also explicitly spelling out what
>> its dependencies (Collaborations) are. Analyzed that way, you might find a
>> more optimal grouping of the responsibilities into modules that gives you
>> less of a dependency head-ache. Even though that grouping may no longer
>> align with your first pass at splitting things up into a modular design, it
>> may be a way to a better design.
>>
>> Occasionally I have had a module that produces 'out messages' to signal
>> some condition that an update method higher up in the structure has to deal
>> with, possibly even updating the state of some other module beneath it, so
>> it is effectively a side-ways message. I can see that this is a bit of a
>> draw-back that modular designs sometimes inevitably lead to. I am avoiding
>> modularizing too early, and using Model-Responsibility-Collaboration to
>> drive the split into modules in a more optimal way, or even deciding not to
>> at all if makes it apparent that I can't find a good modular design.
>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "Elm Discuss" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to [email protected].
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>

-- 
You received this message because you are subscribed to the Google Groups "Elm 
Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to