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].
For more options, visit https://groups.google.com/d/optout.

Reply via email to