I have been working on monkey patching XMLHttpRequest in order for it to re-route specific calls to JavaScript functions and send the return value of these functions as the response of the AJAX call.
Here you have the code:
I will give a good use to this, but I would like to know what people have to say about this before I made the extra work of creating elm and npm packages.
I belive something like this was done earlier and the post was deleted? Making it very easy to interop with js is nice right now, but it is hurting the language in the long run. If great interface with js was a design goal it would have been there already implemented as “task-ports”. ( It is hurting elm in the long run as it makes people wrap js packages instead of creating pure elm solutions ) Also if too many people is dependent of the target beeing js and the target changes you would see anger just like when native was removed from elm. This solution with httpRequests can probably work with any target. But it still lets you easily wrap js packages, and then you depend on that js… And using hacks not intended for the language might result in an impossible swich to the next elm where this is not available. I’m not saying you should stop using this for yourself, but take this into account before making packages and advertising for newcommers to go the easy but non supported route…
I don’t think I’d want this as a package, but I do want to learn from it. I have a relatively specific use case in mind and using ports has been difficult. I’ve been using a JS package built on top of web torrent that has a very http like interface. Due to the nature of this project, while I can learn from and borrow some of your ideas, I wouldn’t be able to use any package you built without even more work on my end.
It is hurting elm in the long run as it makes people wrap js packages instead of creating pure elm solutions
I am using it in situations where all tat can be moved into elm has already been moved into elm. JavaScript code just receives data from a port. Then, as it is received, it is sent to a third party JS API. Finally, the response from the third party JS API, untouched, is sent back to elm. Many of these calls are chained also with HTTP requests. We did our best to make sure that nothing else can be moved into Elm.
Also if too many people is dependent of the target beeing js and the target changes you would see anger just like when native was removed from elm.
If the target stops being JS it will become WebAssembly, in which case the patch will still apply, since webassembly interaction with the browser is limited and javascript will still be needed to make AJAX calls. One different thing would be that the target stops being the browser, but I pretty am sure that is not what you meant.
And using hacks not intended for the language might result in an impossible swich to the next elm where this is not available
Do you think that Tasks will be removed? I think that if they will they will come with a change that will make my patch unnecessary. For simple use cases where interop is limited, having Msg for every little thing can seem fine, but when creating Msgs becomes boilerplate, you know you need tasks.
Let me elaborate on that. In a single view we have different actions that have between 3 and 6 chained side effects (HTTP calls or JS INTEROP) and many of the side effects are shared between the processes. Let’s take one example chain of side effects:
GetAccessToken → HTTP
Action1 → JS INTEROP
Action2 → JS INTEROP
Action3 → JS INTEROP
Action4 → JS INTEROP
StoreResults → HTTP
Approaches we took:
Create a different pair of ports for each different context in which we are using the same actions in the same view.
Also, create a different message for the GetAccessToken depending on the path to initiate. For example, GetAccessTokenForDelete, GetAccessTokenForUpdate.
This leads to a very delicate update structure, in which you have most of the update function being a very uncomfortable way of managing a sorted sequence of steps.
We created something we called Procedure. You create a list of mini update functions that just cover one case. Whenever a message is received, first it is given to the procedures to see if there is a sequence waiting for the message, if not, is is passed to the main update function. It is much better, but still adds the need of having to create Msgs for every step, which are then ignored if they happen to reach the main update function. But at least we can reuse the same Msgs (and thus, the same subscriptions) in different procedures without having to add any if or case to handle the different situations in which the same Msg could have been triggered.
Type safety doesn’t help us here.
But still, we wanted to get better, so I started to think about crazy ideas like creating an echo server just to bridge elm and JavaScript, and then it is when I realized that I could simply have XMLHttpRequest proxy JavaScript calls that go to a specific domain. In fact, I renamed the project from elm-effects-task to elm-effects-proxy, because it seems more meaningful.
We have all the benefits of elm/http: encoding, decoding, error handling, timeouts.
We need only one Msg per operation we want to perform.
Sounds like you know what you are doing. For info: I think maybe that older solution used a plain service worker, intercepting the requests without any patching. I think your usecase is a great example: If it was super easy to use your third party javascript api, you would just do that and be done. But as js-interop is possible but cumbersome to do in elm, you would actually consider creating an elm version of that third party api so that you can stay in happy elm-land all the time. And if that third party library was something like google firebase or some other third party api that more users would want to use, that would be a great elm package making the elm ecosystem better. But ofcourse, not all third party api’s make sense to port to elm, so your solution might be great for your usecase
Now that elm/browser is its own thing I don’t think it unlikely that Elm will target a backend VM our native code in the future. How far in the future is a different matter.
It would be much simpler for me to mimic http request directly from the elm side. On the JS side I want to hook the web torrent library directly into the XmlHttpRequest, somewhat like what you’ve done. I’d prefer not to have another library in between, if that makes sense.
This is one of those cases. Just in case you are curious, the communication with this API is through an iframe using postMressage, so replacing the api call would also need ports to trigger the postMessage and listen for postMessage events; there is no escape JS interop in this scenario.
I we use the same thing, we can directly benefit from each other. If we don’t we can learn from each other. Since you say you are doing your implementation, feel free to ask any questions about what I did or provide any feedback.
I’m going to be doing this for my work app, though I’m going to stick with just ports. That app is already too complex with multiple AWS libraries being used.
I definitely will! This is the project I’m working on where I want to use this approach, and this is the package I’m using that has a HTTP like API.
I used this technique for some sever-side stuff (because XMLHttpRequest isn’t available in Node anyway) but i also don’t think that any packages should be published to the official repository that depend on this.