> > I now need knowledge of the sequencer to flow through the program in a way > that effects managers avoid. >
I disagree with this statement. I was hoping that by iterating through this solution, we would eventually come to an agreement re: the claim quoted above. But you are annoyed at me for treating your problem seriously, and you are annoyed at Richard for dismissing your problem, so I don't really know where to go from here. On Fri, Aug 26, 2016 at 11:14 AM, Mark Hamburg <[email protected]> wrote: > As I said, I know how to write the queue if that's what I really want. > > My problem is that when a module uses the WebSocket.send function to > create a command, I don't have to do anything special in my program to > arrange for that command to make its way to the web socket effects manager. > I just need to route it to my top level update function up through however > many layers of the Elm architecture my app uses. When I want to use my HTTP > fetch sequencer (again a contrived example that I had hoped would be simple > enough not to get buried in discussions of how to build the sequencer), I > now need knowledge of the sequencer to flow through the program in a way > that effects managers avoid. I'm looking for a pattern that allows me to > construct service APIs using the same sort of conventions used by effects > managers and with the same sort of ability to not muck up the code with, > for example, needing to change lots of uses of Cmd.map tagger to > something more like Cmd.map (Wrapped.map tagger). > > Mark > > On Thu, Aug 25, 2016 at 10:37 PM, Nick H <[email protected]> > wrote: > >> OK, here I am going for the obvious implementation again. Sorry the >> formatting is a little nutty. >> >> type Action >> = HTTPResponse String >> | SomethingElse >> >> type alias Model = >> { pendingFetches : List (Cmd msg) >> , waitingForResponse : Bool >> } >> >> >> doAThing : Model -> List (Cmd Action) >> >> >> update : Action -> Model -> (Model, Cmd Action) >> update action model = >> case action of >> SomethingElse -> >> case (model.waitingForResponse, doAThing model) of >> (True, newFetches) -> >> ( { model | pendingFetches = model.pendingFetches ++ newFetches } >> , Cmd.none ) >> (False, head :: tail) -> >> ( { model >> | pendingFetches = model.pendingFetches ++ tail >> , waitingForResponse = True >> } >> , head ) >> (False, []) -> >> ( model, Cmd.none ) >> >> HTTPResponse value -> >> let >> newModel = processResponse value model >> in >> case newModel.pendingFetches of >> head :: tail -> >> ( { newModel | pendingFetches = tail }, head ) >> >> [] -> >> ( { newModel | waitingForResponse = False }, Cmd.none ) >> >> On Thu, Aug 25, 2016 at 9:39 PM, Mark Hamburg <[email protected]> >> wrote: >> >>> Yes, a command queue is the obvious implementation for what I identified >>> as a contrived example. The core problem, however, is how items get into >>> the command queue through the normal command routing mechanisms. >>> >>> Mark >>> >>> On Aug 25, 2016, at 8:18 PM, Nick H <[email protected]> wrote: >>> >>> We might generate any number of these commands during a single update call, >>>> but the mechanics of their execution demand that we not start the HTTP >>>> fetch for one until the HTTP fetch for the previous has finished. >>> >>> >>> One solution that comes to mind is adding a command queue to your model. >>> Something along these lines: >>> >>> type alias Model = >>> { pendingFetches : List (Cmd msg) } >>> >>> update action model = >>> case action of >>> HTTPResponse value -> >>> let >>> newModel = processResponse value model >>> in >>> case newModel.pendingFetches of >>> head :: tail -> >>> ( { newModel | pendingFetches = tail }, head ) >>> >>> [] -> >>> ( newModel, Cmd.none ) >>> >>> On Thu, Aug 25, 2016 at 5:51 PM, Mark Hamburg <[email protected]> >>> wrote: >>> >>>> I'm going to try to take the large app design questions and focus them >>>> on a more narrow and admittedly contrived example. >>>> >>>> Say the people doing the client coding needed to be able to take a URL >>>> string and fetch a string via HTTP. (Yes, this is covered in the HTTL >>>> module. Bear with me. I'm trying to keep the example simple.) Dealing with >>>> tasks all over the place muddies up the client architecture that would >>>> otherwise focus on commands for external operations. So, we define: >>>> >>>> getStringCommand : >>>> (Http.Error -> msg) >>>> -> (String -> msg) >>>> -> String >>>> -> Cmd msg >>>> >>>> >>>> This is easy to write given the standard libraries. >>>> >>>> But now it turns out we would like to execute these one at a time. We >>>> might generate any number of these commands during a single update >>>> call, but the mechanics of their execution demand that we not start the >>>> HTTP fetch for one until the HTTP fetch for the previous has finished. (I >>>> said it was contrived. Maybe we want to automatically fail subsequent >>>> commands if the first one fails.) >>>> >>>> From what I understand of effect managers, we could write an effect >>>> manager to do this but the documentation around effect managers discourages >>>> reaching for them as a solution. They are identified as being for library >>>> writers and though this serialized string-fetcher seems a bit like a >>>> library in its usage, it also feels like a chunk of general app >>>> functionality. Or maybe the backend needs to use web sockets instead of >>>> HTTP and we would like to use the web sockets effects manager as part of >>>> the implementation. >>>> >>>> One way to address this is to replace commands with requests, recognize >>>> string fetch requests when we reach a certain point in the model hierarchy, >>>> and process them accordingly generating commands as we move up the rest of >>>> the hierarchy. This has been covered in previous posts to the discussion >>>> list. The downside to this is that it doesn't interoperate well with code >>>> that wants to speak in terms of commands. One nice thing about effects >>>> managers is that the addressing of a command to a particular effect manager >>>> is essentially unseen by everything that handles it until we get to the app >>>> runner. Having lots of code need to switch from returning commands to >>>> returning requests is a very visible consequence of using this service that >>>> speaks via requests. >>>> >>>> Another way to handle this is by changing update functions so that they >>>> still speak commands, but they now have a signature like: >>>> >>>> update : Msg -> Model -> (Model, Cmd (Wrapped Msg)) >>>> >>>> >>>> We can then watch for wrapped commands and somehow unwrap the ones that >>>> really are looking for work by the sequencer code. That said, I'm waving my >>>> hands somewhat fast here and while we now continue to use commands, we >>>> don't use them in the way we're used to so I don't know that it's a big win >>>> over the requests approach. >>>> >>>> Is there a better way to do this that I'm not seeing? The example is >>>> contrived but so are most examples. It feels like it gets at the sort of >>>> problem for which there ought to be a design pattern — i.e., structure your >>>> types and functions like this to solve this sort of problem. >>>> >>>> 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. >>>> >>> >>> -- >>> 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. >>> >>> -- >>> 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. >>> >> >> -- >> 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. >> > > -- > 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. > -- 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.
