Thanks for writing this up Mark, it seems very clear. It seems like it would be especially good for cases where results of effects need to be applied further up the state tree from where the effect is triggered. Or for cases where you want to examine (and possibly modify) requested effects further 'out' before they get converted to commands -- as in your example of needing to add credentials further 'out'.
Personally I have only occasionally found a need for this kind of thing, but maybe that's for lack of a suitable hammer :) Those helper functions certainly make it look more appealing... But I'm not sure I could evaluate it without actually trying it. Have you come across cases where it seems more awkward than standard TEA? On Friday, November 4, 2016 at 2:06:10 PM UTC-4, Mark Hamburg wrote: > > A note on where I'm moving with respect to structuring code (since many > have raised similar questions)... > > We don't have a particularly deep hierarchy but there are some fairly > distinct levels around things like app state management — are we logged in? > — and then view management while logged in. Even just having a few levels > made it worthwhile thinking about how to structure their relationships. > > I'd been moving toward the inter-module communication pattern supported > here: > > http://package.elm-lang.org/packages/folkertdev/outmessage/latest > > > in which update (and init) functions return not just a model and a command > but a model, a command, and out messages in some form. The outer layer then > applies the inner out messages to invoke further changes. > > One thing that didn't seem to be as well supported in this was having the > out messages generate out messages to go up to the next layer. Thinking > about adding that in and about any implied sequencing guarantees moved me > to a pattern in which the update functions have the following signatures: > > update : Msg -> Model -> ( Model, List OutMsg ) > > (We should arguably change Msg to InMsg for symmetry but prevailing > convention uses Msg.) > > An update to an inner model looks something like the following: > > update : Msg -> Model -> ( Model, List OutMsg ) > update msg model = > > case msg of > > ToInner innerMsg -> > > let > > (newInner, innerOutMsgs) = > > innerUpdate innerMsg model.inner > > in > > ( { model | inner = newInner }, [] ) > > |> applyMessages > > applyInnerOutMsg > innerOutMsgs > > > Where we have support functions: > > applyInnerOutMsg : Inner.OutMsg -> Model -> ( Model, List OutMsg ) > > applyMessages : > > ( msg -> model -> ( model, List outMsg ) ) > > -> List msg > > -> ( model, List outMsg ) > > -> ( model, List outMsg ) > > applyMessages apply msgs state = > > List.foldl > > (\msg ( oldM, oldOut ) -> > > let > > ( newM, moreOut ) = > > apply msg m > > in > > ( newM, List.append oldOut moreOut ) > > ) > > state > > msgs > > > The benefits of this approach are that we can decide at each level how to > process the out messages from the next level down including both making > local changes and passing out messages up to the next level. > > This approach seems to embrace the "Effects as Data" pattern recommend > here: > > https://www.youtube.com/watch?v=6EdXaWfoslc. > > > In particular, it does so in a way that the standard use of commands seems > to thwart since commands aren't particularly designed to be inspected. > > But still, we have to fit commands in somewhere. At the top level, we > can't return a list of out messages and instead must build a command. One > way to do this is to make Cmd Msg the out message type for the top level > and then as a last step apply a Cmd.batch to the list of out messages. > Further down, while potentially less inspectable, we may still want to > generate commands without the intervening levels getting involved (though > this means that it's harder to put those lower levels in a test harness). > We can do that by including a Command (Cmd Msg) in the OutMsg type union. > The next layer out then uses Cmd.map to change the message type and > rewraps it in its own Command OutMsg. This pattern for handling commands > also makes it easy to move existing code over to this out message centric > model. > > I'm still in the process of converting code over, but I'm feeling pretty > happy with the results so far and conceptually it feels easy to explain: An > update function takes a message and a model and produces a new model and a > list of out messages to be handled at the next level up. If you need to do > something that doesn't fit within an update to your model, generate an out > message and let the next level up handle it. If you need information — e.g. > credentials — at a lower level to construct a request, don't do the final > command construction but rather return an out message describing the > request and let the level that has the credentials do the construction. > > Mark > > -- 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.