In my experience - the quality level of javascript packages varies vastly - and not all of it depends on years experience - much of it depends on the principles and practices of the library author - testing, clear interfaces, and careful responsibility segregation without over doing things is easier with experience - but even a rough attempt that follows those kinds of principles is a league ahead of most javascript today.
Sometimes the simplest solution is the best and while it can be attractive to modify the core to support things that appear to be “simple” it’s not alway really actually simple to do so.
For instance in the link you mention: https://github.com/elm-lang/elm-compiler/issues/1450 - the last post mentions using a single Ports.elm - it would give a developer a single point of change and management for the Elm application - thats seems nice - straightforward (after some effort is applied to get the ports subscriber concept down) - and some tradeoffs are accepted.
I think it’s nice to have libraries - but even more so it is good to have code that will not throw a runtime error - this is something truly radical that elm gives. If you need javascript and elm together in your app - you are probably the best person to understand that context - Yes it means learning about subscriptions - and yes it will mean managing concurrency issues that can be created with this kind of event model (too many events, not enough, race events etc) - honestly that kind of thing happens in javascript all the time - Elm is better and ports and elm is better than just javascript – and that’s what we have for now.
I think perhaps for now - a good way to use elm at the moment - is to think of it as a place to put the essence of your application - core logic - and then perhaps integrate a bunch of scrappy subject-to-change javascript around it. That’s where my head is at with javascript - mostly because I work with a team of developers who know javascript well and it works for them - in order it bring elm in I really need to be focused and make it’s power work in a way that makes it worth the extra rigor it brings.
Ports is a good way to get comfortable with the publish subscribe pattern - It may not be ideal - but it is effective - and ultimately since so much of software is making careful, mindful trade-offs I think “good enough” is a great starting place for many things.
A single Ports.elm file is what I have in all my apps. But I went further and used only two ports in the Ports.elm. This is a good pattern and what this proposal amounts to is to standardize and formalize this as the only way to have ports in Elm.
Well, I’m just going to give an example here. I integrated javascript in a “hackish” way in elm-file-reader 2.0.1 and if you look at the javascript there, it has special cases to work in browsers that use slightly different api:s etc. If I, as a user, want to implement a file control, I really don’t want to deal with that. I want to put the file input there and get the file contents when the user selects a file. Handling compatibility issues, the different web api:s FileReader, File, DOMException, FileError, CustomEvent, the peculiarities of HTML5 drag and drop etc and packaging them into a simple to use API is exactly what a package should do for you, in my opinion. So I made that package.
I can understand being frustrated about wanting to have a package like this - and then not having any clear way to support the code you wrote in Elm naturally.
However maybe look at what you would need to do to maintain this library over time? What would you tell people that found bugs in your on-change handler of wanted features that this library didn’t support ? How would this library change and grow over time?
FileReading seems like an Effect in the elm world - and likely will need to be implemented as Kernel code : and is going to need to manage concerns like this websocket code does:
The story isn’t “no native” it’s Kernel now and it’s going to have different concerns - otherwise as nice as your library is trying to be - it’s actually just creating work for-yourself in the future…
That said - maybe there is a way for you to get involved with Kernel code questions and start contributing experiments with API’s that will need to operate in the context of Effect managing and the scheduler.
Well this is the responsibility you take on as a package creator and maintainer. I’m not sure how this changes because there is inline javascript. If people find bugs I will do my best to fix them.
FileReading is an asynchronous operation, for sure. This is why it sends a message when the file is read in. After that, there is no need to keep any state etc.
My view is that Ports should be cumbersome and a bit hard. Last resort type of thing.
If ports is super easy, that is what you go for. There is no intensive to create pure Elm modules.
Even if someone fixes your issue in a new pure Elm module, there is no need for you to remove your port.
Ports should be a bit hard to do so that you want to remove them whenever possible. And make the Elm Ecosystem better
We are talking about the purpose of ports - I confused Norpan with Pdamoc - One was suggesting a change to Ports, the other was avoiding use of ports in favor of exploiting a design flaw.
Native code that uses an async api like Filereader and integrates into the scheduler as an effect - is a way to shim something that would otherwise be done with ports.
Just so I make it explicit, what I’m suggesting is to keep the ports but instead of allowing users to define their own ports standardize on 2 ports (input and output) and offer access to them through either a Cmd/Sub combo or a Cmd/custom program combo.
@pdamoc this post had a couple of points on what a good process for your suggestion could look like. I think the ports proposal could be fleshed out more concisely - I’d like to do that, perhaps as a GitHub repo of documentation and examples?
Be my guest. Contact me on Slack or here if you have any questions I might be able to answer.
The key resources so far is Murphy’s talk on ports and my own elm-ports-driver. The driver example shows how a 2 ports solution can be used for multiple purposes. In short, it is like creating an update function on the other side of the ports and having the ports be just a means of message passing between the Elm update and the JS update.
I’ve done experimentation recently, as a result of this discussion.
I’ve taken the design in a different direction, as the goal has changed into “how far can I get with the current port interface”. This quickly turned into "how can I implement something along the lines of:
I’ll interject with my solution here. A single Cmd/Sub port pair, with code on both the Elm and JS side to do the dispatching. I’m converting all my port code to use it. If Elm itself enforced the use of a single port pair, it wouldn’t bother me a bit.
I’m not sure whether it was considered before, but I have a strong use case for multiple incoming ports.
I was using elm-lang/mouse along with the moves and ups subscriptions when I noticed that I need to handle touch events as well. So I wrote a similar interface for touchmove and touchend events that uses ports.
I could, of course, multiplex all of the touch events over my other port, as I do already use the “one port for all communication” technique in my app. The problem is that I’m actually only rarely interested in the touchmove and touchend events. Only when user drags my component around do I actually subscribe to these ports, this saves me quite a lot of otherwise unnecessary computations. Touchmove events come in quite a high rate, need to be converted to Elm just to be thrown away if they aren’t currently needed.
So I really do need to have at least two incoming ports in my app to be able to selectively subscribe to one of them for this performance optimization to work. Any ideas how it could work with just one port?
It depends on how you view things. With this pattern I like to view the interaction between JS and Elm as a dialog between 2 actors. The messages from Elm have some kind of effect in JS and the messages from JS have some kind of effect in Elm.
Toggling a subscription to some messages is just one of the effects that can happen in JS.
Another way to put it is that, instead of having a flag in the Elm model and selectively subscribing in subscriptions, you have a flag in the JS model and you selectively send messages to Elm.