I’m looking for some help in how best to avoid writing extra code on the javascript-side of a port. I would love a general purpose strategy to take forward, but becuase I can’t explain very clearly what I mean (and because I might just be attempting something stupid!) I’m going to give a specific example of something I’m doing where it came up…

I’m trying to write a ports module to access a subset of PeerJS’s functionality, just for personal use at the moment. Specifically I want to be able to send data between different users. PeerJS provides a broker service that assigns clients an id to find each other and then helps set up a web-RTC connection between them.

However, I’m a bit of a perfectionist and even though the code is just for me I’d like to do it “the right way”, especially as I may end up using the module in multiple projects.

With this in mind I definitely don’t want to just “wrap” the PeerJS functions I need, but instead produce something more Elm-like. After much thinking I decided I wanted an API along the following lines…

A user of the module must first create a type for the data they will be exchanging and create JSON encoders and decoders for it.

They pass these encoders and decoders to a module function to create a RegistrationRequest.

Once they have a RegistrationRequest they can use helper functions to add any additional options they may require (e.g. requesting a specific peer id with requireId) and then submitRegistration on the request and waitForCompletion to get a response. On successful registration they get a Broker data back. This is the only way to create a Broker, to prevent accidentally trying to start peer communication before everything is set up.

Once the module user has a Broker specific to their data type they can start to connect to other clients, wait for incoming connections and send data across existing connections. This is all done by calling functions on the Broker data they hold which return a Cmd msg. They subscribe to the responses to all these commands by using the subscription they get by invoking responses on their Broker…

Note that the Event is specialised according to their chosen data type. There is no need to fill their code with manually calling coders/decoders but rather they get the the result of trying to decode the received data using the decoder they specified at registration.

I was very happy with all this (but feel free to tell me if you think it’s a rubbish API!). However, the problems came when I actually started trying to implement it…

I decided I wanted the module to be as user-friendly as possible. If the connection to the broker drops it should try to reconnect automatically and only issue an Error if it fails. If a Connection to a specific peer goes down it should try to establish a new one for the user, and automatically accept any reconnection attempts from that peer, etc. But in doing all this I found myself contemplating writing more and more javascript on the other side of the port to make it happen.

Now I’ve never learned javascript, I muddle through it based on experience in other languages. I came to Elm specifically because I didn’t want to have to learn all the eccentricities of javascript. And so I couldn’t help thinking, surely it would be safer, easier and more pleasant to be doing all this on the elm side of the port…

But that’s where I hit a problem. The only way I could think of to implement such things on the elm side was to have some kind of event/update loop within the module. I would have to add an extra possibility to my Event type to deal with internal messages like so:

But what is to stop a user (or more importantly me, six months later when I want to use the module for something new) from just quickly trying to knock something up in their update loop and forgetting this? E.g.:

I eventually came across the idea of writing an effects manager to let me deal with these messages without any fear of the user forgetting to, but it seems that approach is now frowned upon and won’t be available in 0.19. So is there any other way I can ensure that these messages get handled (other than handling it all on the javascript side of the port)?. Or am I just being paranoid about how careless my future self (or others if I ever decide to share it) will be when using the module?

Any advice on any of this would be greatly appreciated. Sorry for such a long post, but I wanted to follow the advice about the “XY problem” and being specific about what one is trying to achieve!

providing as input a msg to receive any resulting Event and a msg to for any further RawMessage as well as the message to process and then receiving in return a new (potentially updated) copy of the Broker. To thus get access to any results from the Broker the user is now forced into an update loop as follows:

Just in case it slipped through your searches, this discussion had interesting examples of how to make retries automatically by chaining tasks in one unique command. It might help in your quest to reduce the amount of JS code, or it might be useless.

I needed a generic way to retry requests that fail. I did not want to litter my model with extra state or clutter my update function by having to handle additional cases for each request’s code paths.
I found panosoft/elm-cmd-retry but it depends on native code and meets neither of the requirements set above.
Wishful thinking
It should be intuitive to use
decoder
|> Http.get url
|> Retry.retry
|> Task.attempt DataReceived
It should allow us to retry with configurati…