The part that is "more awkward" is in having to write the functions to apply — or even just translate — the out messages from one layer to the next. But, of course, the opportunity to do that work is where the pattern gets its strength, so it's a tradeoff.
Other than that, it could probably benefit from a few more utility functions and possibly a replacement for List as the way to return sequences of out messages but I expect those to reveal themselves with experience. Mark On Saturday, November 5, 2016, Eric G <[email protected]> wrote: > 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 [email protected] > <javascript:_e(%7B%7D,'cvml','elm-discuss%[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.
