I know the point was "this is a bad type signature", but I guess I'm still confused. I can't think what problems someone is trying to solve by writing a function like this.
I can maybe see why it might have both `Model` and `PersonalInformationModel`: the author doesn't want to prefix everything with `model.personalInformation` so they pass both `model` and `model.personalInformation` to it. (Maybe they forget that they can start the function with `let personalInformation = model.personalInformation`?) So I can maybe guess why `PersonalInformationModel` might be there, and if my guess is right I agree it's silly.
But I have no idea what the `Cmd PersonalInformationMsg` is doing, and it makes me think I don't understand the `PersonalInformationModel` either. Where does that input come from, and what does the function do with it?
If it seems confusing, it's because it is. That's the point I'm trying to make. I have seen — several times across a number of projects — people writing code with a philosophical ideal along the lines of "well, this page should only know about the state (a subset of the model) that it is responsible for, so I won't give it the whole model. Also it will only send the messages related to just this page. Oh, requirements changed. No problem, I'll just add another part of the global state as an argument. Oh, requirements changed again, and I need to send a global message. Hmm… I'll just… etc"
Furthermore, I've observed over a few years the behaviour in which developers with significant JavaScript ecosystem experience will impose these philosophical ideals far too early in an Elm application's design because they aren't familiar with Elm's refactoring story and are justifiably wary of a codebase growing to the size and level of complexity that is beyond saving.
I understand and agree with the idea of generally narrowing types to simplify APIs, but in my experience, the typical "page" in a multi-step webform actually contains too many cross-cutting concerns for this approach to remain frictionless as business needs change (and if your business/product needs don't change, then I would like to buy whichever crystal ball you're in possession of).
> If it seems confusing, it's because it is. That's the point I'm trying to make. I have seen — several times across a number of projects — people writing code with a philosophical ideal along the lines of "well, this page should only know about the state (a subset of the model) that it is responsible for, so I won't give it the whole model. Also it will only send the messages related to just this page. Oh, requirements changed. No problem, I'll just add another part of the global state as an argument. Oh, requirements changed again, and I need to send a global message. Hmm… I'll just… etc"
But... none of that, as far as I can tell, gives you a signature that looks like this? Even making the kind of changes where you think "okay, this is an ugly hack, if I'd known about this in advance I'd never have written it this way, but I don't have time to go back and do it properly so..."
If you've genuinely seen signatures that look like this, then my sense is that the people who wrote those functions have different problems than the one you describe.
Maybe I'm pulling on something that turns out not to matter here. Like, if you've experienced the problem you describe, and you just came up with an example silly type signature that turns out not to really work as an example, then fair enough, that's not a big deal. But if this post was inspired by seeing type signatures looking like that, and trying to explain those type signatures, and trying to come up with advice that will prevent them, then I would guess the advice is mistargeted. Or maybe I'm missing something and the process you describe really does lead to a signature like that, by people who are generally more-or-less competent; I don't say that it couldn't happen, only that I don't see how it could.
(I disagree with the sentiment "If it seems confusing, it's because it is." If people who lack a certain mental model repeatedly make the same sort of mistake, and generate something that seems confusing to people who have that model, it can be extremely helpful for the people with the model to figure out what's going on so they can try to share the model.)
Did you, perhaps, mean something like
updatePersonalInformation
: PersonalInformationMsg
-> Model
-> Result (PersonalInformationModel, Cmd PersonalInformationMsg)
(Model, Cmd Msg)
? That would let a person say "this branch gets to update the global model, but all the others can stick with local changes and I just need to wrap their return value in `Err`".
(Which is still silly, just start the function with
let local : (PersonalInformationModel, Cmd PersonalInformationMsg) -> (Model, Cmd Msg)
local = ...
and wrap the return values in that. But I can at least see what someone would be doing with a type signature like the `Result` version.)
I can maybe see why it might have both `Model` and `PersonalInformationModel`: the author doesn't want to prefix everything with `model.personalInformation` so they pass both `model` and `model.personalInformation` to it. (Maybe they forget that they can start the function with `let personalInformation = model.personalInformation`?) So I can maybe guess why `PersonalInformationModel` might be there, and if my guess is right I agree it's silly.
But I have no idea what the `Cmd PersonalInformationMsg` is doing, and it makes me think I don't understand the `PersonalInformationModel` either. Where does that input come from, and what does the function do with it?