5 ms·
> These functions almost inevitably end up needing something from the top-level application-wide state, so you’ll often see some type signature like this:
by philh 6y ago
> These functions almost inevitably end up needing something from the top-level application-wide state, so you’ll often see some type signature like this:
> updatePersonalInformation
> : Model
> -> (PersonalInformationModel, Cmd PersonalInformationMsg)
> -> (Model, Cmd Msg)
> This is way too complex already, and this approach doesn’t even actually buy you anything.
Is this accurate? I feel like this may not be what the author intended to write, since the update function isn't given any kind of message.
The author's approach has data for every page of your app stored in the model at once, and available to every other page. Also, any page can edit the data for any other page and send messages to any other page. That makes me nervous.
We went with having a small amount of global state that was visible to every page, and that every page could update (returning (Model, GlobalState, Cmd Msg)). That seems... basically fine? It's still a bunch of boilerplate, but not that much, and I don't think we've had significant problems with it. We need Cmd.map and Html.map but I don't know why the author thinks this is bad.
(The boilerplate is mostly turning `(model, cmd)` into `(model, globalState, cmd)`. On the other hand, we don't need to wrap each of our messages with `ThisPageMsg`. Most pages don't need global state, and we let them use a regular Msg -> Model -> (Model, Cmd Msg) update function.)
(Oh, I think very occasionally we do need to send a message up to the top-level as well. I don't remember exactly how we do that. It might be something like... those pages' update functions take a `PageMsg` and return a type `PageMsg PageMsg | GlobalMsg GlobalMsg`, and the top-level update function interprets `ThisPageMsg (GlobalMsg ...)` and passes through `ThisPageMsg (PageMsg ...)`. Again, boilerplate, but not significantly problematic, and only on pages that need it.)
- yakshaving_jgt 6y agoAuthor here. You're right, there was a line missing. That's now fixed. Thanks for spotting that. To address your other points: in just about every Elm application I've ever worked on, there was an initial ambition to separate and encapsulate things nicely, for reasons of safety or Clean Code or some other philosophical ideal. Then requirements change (which is normal and good — businesses change and evolve, and the product should mirror that). Then you discover that oh, you need a bit of state from one page in another. Oh, and modifying the state of something on one page should affect this other page. Hmm… Why does this view function now take seven arguments? Your final paragraph hints at the kind of engineering that inevitably begins to appear as a workaround to solving the wrong problem in the first place. All of it is extra complexity that doesn't need to exist at all. > The author's approach has data for every page of your app stored in the model at once, and available to every other page. Also, any page can edit the data for any other page and send messages to any other page. That makes me nervous. Why exactly this emotional reaction specifically? Does prior experience with bad surprises in Elm give you this intuition? Or is it from experience from a language like JavaScript where global mutable state is indeed something to be wary of?
- philh 6y ago> Your final paragraph hints at the kind of engineering that inevitably begins to appear as a workaround to solving the wrong problem in the first place. All of it is extra complexity that doesn't need to exist at all. I agree it's extra complexity, and it's not strictly necessary. But it's not that much extra, and it doesn't affect pages that don't need it, which in the app I work on, most don't. (Whereas if all your pages take a top-level model, and return a top-level message, they all need to deal with that even if they don't need it.) > Does prior experience with bad surprises in Elm give you this intuition? Or is it from experience from a language like JavaScript where global mutable state is indeed something to be wary of? Not specifically Elm, I've only written it in one app. But I'm imagining bugs where a page mutates its state in ways that satisfy some invariant, and then that invariant is violated and no one can work out how it happens. Turns out some other page is editing its state, in ways that probably made sense at the time. (Granted the debugger will probably help, but... I dunno, I'd rather just not have to.) I'm not convinced large Elm apps don't have problems like this. And saying "no, if you want to communicate with another page, you have to do it through this specific mechanism" seems like a fine way of avoiding it.
- pyrale 6y ago> Turns out some other page is editing its state The solution to this particular problem is to stop thinking in terms of page. Which page you're on is only a matter of view, and maybe a minimal amount of view-related model. Most of your model data should be related to your application logic, not to a specific page or component. Separating your model logic and your view logic helps a lot to avoid having this problem, because your model logic becomes agnostic to what page modifies it.
- philh 6y agoI've never tried it your way, so maybe I'm missing something here. But thinking in terms of pages seems like a very useful abstraction to me. I can't keep the whole application state in my head at once, so I want to be able to look at a small part of the app and know that I don't need to think about anything else right now. (It maybe won't be "literally just this page", but "this page plus a small number of well defined mechanisms for bringing data in and out of it".) I wouldn't want to give that up, and I don't know how I'd split the state if not by page (or at least something similar, like "groups of closely related pages"). If my model logic is happy to be modified by any page, and if there's a bug in my model logic, then I have no idea where to look.