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 <mhamburg...@gmail.com>
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 <
> elm-discuss@googlegroups.com> 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 elm-discuss+unsubscr...@googlegroups.com.
>> 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 elm-discuss+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to