I’m starting to build a side project in Elm, which is a SPA, to learn more about Elm. I have a backend that serves up a GraphQL endpoint that I can query for my data via HTTP requests.
In keeping with the theme of using types to avoid impossible states, I have been using the basic pattern of type Model = { data : WebData MyDataStructure }.
While I am finding this works well for the initial load of the data to force me to properly handle error states, etc., it seems quite difficult to deal with when I want to update MyDataStructure. It seems like if I want to do that, I’d have to do a case ... of for my current model a lot, even in cases where it seems really unnecessary (e.g. that message can’t even happen if the model isn’t loaded).
Is there a better pattern here or what am I missing? What alternatives have you used?
There has been a discussion a while back which I cannot currently find. If I remember correctly the essence was that this pattern is good enough for many usecases but might not fit yours. Then again it is hard to generalize a more sophisticated Datastructure there because basically having a ‘history’ of past requests can get very complex:
NotAsked
| InitialLoad
| Reload a
| Failure e
| ReloadFailure a e
| Success a
This might fit your usecase, or maybe you need the previous content of a success in the reload success to make a diff. Or you need the previous error for the reload success…
I am not dismissing your concern, I just think it might make sense to tailor this to your needs inside of your code instead of a library.
Solutions that come to mind are:
Fork WebData to accomodate for your usecase (see above)
Have a List (Webdata MyDataStructure) or lastGoodData : Maybe MyDataStructure
This might not actually be what you want, but have you seen those GraphQL libraries?
map is basically a shortcut to say “please run this function on this data, if the data is in a ‘success’ state”. map is provided for many data types, for example, Maybe.map and Task.map. And also for types like Cmd.map, where there’s not a ‘success’ state, but it can be useful to access the internal value.
Hopefully that makes sense . I could create another example with more context if that would help.
Thank you. The data structure you’ve outlined there is sort of what I started to go over in my head, which is actually what prompted my question, as I began to wonder if that was overly complicated and convoluted. There comes a point where if I’m going to display what data I have in any case, there isn’t much point in hiding it in a type and I can simply maintain the error state elsewhere.
I have in fact looked briefly at both libraries, though I’m not sure that either is really solving a real problem that I hope to solve right now. Making queries to GraphQL is dead easy and already effectively type safe with a lot of tooling.
An interesting approach is actually this one, https://github.com/jahewson/elm-graphql, but I believe it’s not up-to-date with the latest Elm version. This package aims to generate decoders and requests based on a .graphql file and based on the actual types within the GraphQL server. This is a great approach (and one I’ve read about with other types of REST backends that are strongly typed).
I’m not (yet) finding the boilerplate and maintenance of decoders troublesome enough to bother with more dependencies.
Thanks! Yes, I think this could be extremely helpful. I figured there was something like this that I was missing. I’m still not familiar with all the patterns for dealing with these ADTs.