For monad heavy code I tend to use a modified elm-format[1] that doesn’t indent code after <| \foo ->.[2]
This allows me to use do m fn = andThen fn m like this:
Oh cool, that’s at least a good work around for using the elm-do package. Still feels weird using it for Elm though, since most other people in the community won’t understand it.
Example of the same approach with the `(, Cmd msg)` monad
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
let
( newA, aCmd ) =
updateA msg model.a
( newB, bCmd ) =
updateB msg newA model.b
in
( { model | a = newA, b = newB }
, Cmd.batch [ aCmd, bCmd ]
)
The downside is it obviously takes a huge amount of vertical space. Also it doesn’t give a clean pipeline ordering to the flow of information. But I think there are some benefits:
it (arguably) makes it easier to focus in detail on what each single step of the code does (and I have some minimal evidence to think that this is less daunting for new developers to understand what’s going on)
it allows for even more flexibility in exactly how any errors are combined (though admittedly that’s rarely needed)
it lets you have more of a branching structure of data dependencies (as opposed to being required to thread all data through a single pipeline sequence–though again, that kind of flexibility is rarely needed).
Though I agree, I wish there were consensus on a go-to approach for doing this sort of thing in Elm. All the available solutions seem very clunky compared to do-notation.