Why we chose Elm for Humio’s web UI(humio.com) |
Why we chose Elm for Humio’s web UI(humio.com) |
Complex UIs are a breeze in Elm. We have 140k lines of code of Elm so far on that one product (there are also internal admin interfaces that I don't count here) and I can't imagine writing and maintaining something like that in React+TypeScript or some other JS UI library. (Mentioning React because I have experience with it. Dunno about Vue/Svelte/others.)
Wrt. hiring, we are experiencing the Python paradox: the last time I posted a job in the #jobs channel in the Elm Slack, I had about 8 solid applicants reply in the matter of hours/days. From my perspective, people are absolutely eager to work with Elm full-time and we don't have the problem of having to sift through totally junior/beginner candidates.
Re the Elm development model: I tolerate the fact that PRs don't get picked up immediately and that work gets done in batches. It is sometimes a bit frustrating and contrary to default expectations from other open-source projects, but it's not the end of the world. In the ~3 years of working on our app I haven't found a situation where we'd be blocked without some kind of escape hatch (typically ports and WebComponents).
Re community: I can again only sing praise about it. Yeah it might be protective of the best practices and idioms, but the folks are incredibly helpful. If you are willing to listen, there will be enough advice to save you from digging yourself into a hole (native JS modules etc.) and writing a heated "Elm sucks" article later.
Overall, Elm is both what I write small experiments and spikes in, and large production-ready applications. Not to mention side-projects. 10/10 would start an Elm project again :)
Those Ember diehards definitely still exist too, I work with a lot of them. They, like a lot of the Elm die hards, are also really kind people and great devs. The main difference I've seen is that Elm devs get to spend most of their time building features while the Ember devs end up spending much of their time trying to keep up with Ember and a lot of time dealing with project build bugs and infra.
I've also been writing React on and off for around 6 years and can confidently say that React doesn't suffer from maintenance issues in the same way that Ember does. Where it does fall short of Elm, even when including TypeScript, is both speed (both build and runtime performance) and ease of refactoring.
I've also spent the past 2 months working on a small-ish Svelte + TypeScript app. That's probably the closest I've come to the experience of using Elm. It's runtime performance is on par with Elm, sometimes faster and sometimes slower. It's build time is still a bit slow, mostly due to TypeScript, and it's onboarding is quite quick for someone who already knows HTML + CSS + JS. It still falls short when it comes to refactoring though as it just can't match the error messages that Elm is able to provide.
Just my 2 cents having used many of the frameworks for many years.
Interop with JS is mostly fine. Just integrated Adyen, and it went without an issue (except their js bundle being 10x the size of our elm app..). Same with our map provider. We can call their APIs, and they can render/take over certain divs inside our elm app without issue. The only JS issue I'm having is for smaller synchronous stuff. Like calling small utils imported from JS is burdensome, as it becomes an async flow and the response can only be handled in our update function. But day-to-day mostly a non-issue.
No real changes for the last two years. But that's fine. No breaking changes (like from 0.18 to 0.19) and not really any big features I'm missing.
I'm very fan of elm-css. There are some minor annoyances, but having the css just be plain elm-code (with type safety even, cannot write invalid css) with functions and logic and stuff is very powerful.
The community, on the other hand, is what turned me off. It does seem very unfriendly to newcomers. In addition, https://lukeplant.me.uk/blog/posts/why-im-leaving-elm/ amongst others, is an entire turnoff. And seems like this perception isn't isolated and has been discussed at https://news.ycombinator.com/item?id=22821447
I hope things work out well for you in this project with Elm, now and in the future. But it doesn't seem like any of the issues with the community have been even attempted at being addressed, so I don't think I will give Elm another try.
Personally, despite several gripes, I definitely find working and especially refactoring in Elm sparks joy!
I wanted to give a shout out to one of Jeroen's other projects in the Elm ecosystem, elm-review (https://package.elm-lang.org/packages/jfmengels/elm-review/l...), it is an excellent linter/fixer and allows writing your own rules as well.
It's not just a fledgling project, it's a project that's being carried by one person for a very broad scope, and it's been going for a very long time now. Contrast that with something like vue, where Evan You has a fair amount of corporate sponsorship, has a much smaller scope (i.e. not an entire new language), big following, and has other frameworks to develop in tandem with (i.e. get inspired by react). Not to mention that it's very similar to other frameworks, so it's easy to pick up, so easy to hire for.
I would shitting bricks if I had 200k lines of code in a completely different language with a heavily different paradigm, that's not really battle tested.
I normally wouldn't bring this up but the article is very specifically talking about the benefits of using this web framework, so it seems apropos.
The downsides of Elm are that the language really does have different expectations than less purely functional lambda-calculus inspired languages. I found dealing with my projects' Msg types, in particular, to be a pain. Once you understand the Elm way of thinking about things it does make sense.
My sense of the community is that they are extremely helpful about showing you the "elm-ish" solution to your problem. They can be pretty obtuse about *why* that is the way to do it. In general, I thought the transition from imperative OO langauges like JS to the lisp-ish Elm was a lot harder than it needed to be. I would ask why my approach wasn't working and get a reply telling me to do it a different way - which didn't really help me understand why my approach didn't work. I can understand why it would be exhausting to try to meet people where they are, but I also get why the "do it this way" approach is off-putting.
The downside of Elm simplicity in comparison to haskell is inability to express even simplest typeclases like Ord, or very useful constructions like Lenses.
I think there were some recent changes to the front-end too that makes it feel snappier than before. Great job on that too.
However, at the risk of sounding like insensitive clod who makes critical comments on a post talking about its front-end, if I can offer one criticism of the web interface it's that it doesn't responsively adapt to mobile resolutions nicely. I get that the primary use of a tool like Humio will indeed be from desktop browser (which it works well on) but it would be nice to do some quick error checking on the go using my mobile.
The points this article makes like strong typing pull me towards Elm, but people talk about understanding Lisps as a nirvana-like state of enlightenment, which pulls me towards Cljs
With the usual caveats around LOC measurements, what are your experiences for frontend codebases, for software that isn't GMail or Facebook?
My experience with large scale front end applications is that they are incredibly hard to build correctly, the complexity involved in modern javascript frameworks is staggering. Elm with its tea architecture for building statefull apps is actually one of the simpler options one can go with.
My Elm experience was that, even with the author of an Elm book on the team, it only took one "generic" component to hit the limitations of the type system.
More specifically, this was for an electric utility dashboard / control application that needed realtime charts for many kinds of values (watts, amps, etc). We were good little Strong Typers and made each kind of unit a tagged type (so "KilowattHours v" etc) and made conversion utilities (kWh -> Wh and so on). Fantastic, no way to accidentally add together kWh and MW to produce nonsense.
But there's no "value with any unit" type the way we set it up - after all, the whole point was that you _can't_ substitute the wrong kind of value - so it becomes impossible to EXPRESS the type of a generic function from "List of values with units" to "graph".
The compiler is a mixed bag: on the one hand, the marketing is true - you can just keep fixing error messages until they stop and your code will likely be correct. On the other hand, "until they stop" is not a great complexity bound; displaying timestamps in "the current user's timezone" in our application required an 800+ line change.
Are you able to parse each type to an integer and graph the output?
I'm not 100% convinced of the benefits of Elm from the perspective of using it in the context of work-- the downsides like being hard to hire people to work in it and lack of external libraries for common tasks seem pretty major compared to the upsides, in the context of work. But I would not consider using anything else for my personal projects simply because it's an order of magnitude more fun to program in Elm. I've tried many JS frontend frameworks and none can compare so that's what I use.
- Haskell / GHCJS
- OCaml / Buckle Script / ReasonML
- F# / Fable
Aside from ecosystem concerns, these languages also offer more server-side features than Elm on Node.js
It's a pity the leadership / dictatorship means package management is a massive pain and support for some features is not available (unless you FFI to JS).
It feels like what react + redux + saga should be like.
Amazing developer experience. The only cons is availability of content online / hiring.
As written in different comment my Elm hiring experience was stellar, but now I wonder if it might be because we're fully remote team (not, say, Czech company hiring only Czechs from a radius of 30km around one city. I can imagine how that would limit the talent pool.)
In my many years of experience, learning a new language or framework or API is simply part of the job description. What matters is critical thinking, software *engineering* experience, aptitude, attitude, a willingness to continue learning, and a desire to help others learn.
Every one of my engineers picked up Elm quickly and has come to enjoy using it. There is literally a world of (future) Elm engineers out there to hire.
I don't think elm-desktop-app is abandoned, just that the author is working on other things at the moment. The author is has been working on another Elm tool, and fixed a couple elm-format bugs too.
Feel free to reach out in the Elm Slack or Discourse if you have any more detailed questions. I know there's been discussions in the https://discourse.elm-lang.org/ around desktop apps too if you want to search there.
The elm part: https://github.com/concourse/concourse
Not sure if you would categorize this as dev tool https://github.com/erkal/kite
And ellie certainly might be a dev-tool, but not a toy https://github.com/ellie-app/ellie
I’ve made a liar out of myself. I needed a GUI for a project I’ve been working on using Elixir. After looking at native Elixir alternatives (I even coded up a prototype using Scenic), I decided to give Elm another shot and I’ve had a great experience both with it and with the community.
For writing web applications Elm is an excellent language to work in, the compiler is friendly and fast, there are basically zero runtime exceptions, refactoring is a breeze, and it really shines on larger code bases. Web-components are discounted pretty quickly in that post, but they really fill an important gap where ports are awkward.
In the years I've been using it I haven't run across a problem that I couldn't solve in a nice way. That is not to say my experience is universal, but the negative posts tend to garner a lot more attention than positive posts, and I'd feel bad if someone were to skip over what is an awesome project due to them!
> the negative posts tend to garner a lot more attention than positive posts
On the other hand, my team was very unsatisfied with Elm and when we bring those issues to community we were quickly asked to leave. Of course it is fair but please note that negative opinions are discouraged and thus not common. We just disengaged from community and started elsewhere.
https://reasonablypolymorphic.com/blog/elm-is-wrong/
https://dev.to/kspeakman/elm-019-broke-us--khn
https://blog.bitsrc.io/elm-and-why-its-not-quite-ready-yet-2...
https://medium.com/@cscalfani/the-biggest-problem-with-elm-4...
I've come to view Elm as someones personal compiler project. If you are good with the decisions made then you're going to have a fine time, otherwise you probably won't
My actual experience has been exactly the opposite: I've interacted with many members of the community (at multiple Elm conferences, in the Slack, and even twice met members of the community for in-person meetings when they were visiting near my office). All of these interactions have been the entirely opposite of "unfriendly".
I posted an article saying I now recommended elm and was a part of the reason we chose it for our product. But I also said some things I disliked about elm.
So I was banned.
No warning.
As a newcomer to the language, I've had a pleasant experience with the community (r/elm and the Slack channel). But I can imagine drama could quickly arise if you were to debate design choices with the team behind the language.
Elm is IMHO extremely friendly, especially to newcomers. The leadership style however is up for debate but that's another story.
I wouldn't bet my company on Elm personally, but I can't recommend it enough for side projects and folks interested in learning more FP or simply for fun.
Rather than start from scratch, perhaps the "GUI browser" can be based on the Tk or Qt ui kits, since they are road-tested and exist.
:-)
It was a stupid bug on my part that numbers got that big obviously but still. Take the no runtime errors with a grain of salt.
I wonder whats the state-of-the-art approach from the FP community and if there's no really good answers, just patches like exceptions are, if in fact is the expectation and the classic assumptions of FP that are actually sort of broken?
I know i'm tripping here, but wonder if we managed to program in a "multidimensional-state paradigm", where you could call a "multidimensional f(x)" where in one dimension of the state it will always be a valid reply, and the side effects would be another f(x) that would solve the side-effects in parallel, but both functions would be intrinsically bounded to each other like in the schrodinger's cat experiment.
(Edit: to be clear here, is about the language itself to automate this, instead of expecting us to manually handling this or resorting to band-aids like exceptions which just forget about the state it was handling before. Its akin to state auto lifetime management as GC's, ref-counts, etc do, instead of expecting us to manually release the state as in a call to C's free())
[0] http://blog.jenkster.com/2016/06/how-elm-slays-a-ui-antipatt... [1] https://guide.elm-lang.org/effects/http.html
We have over 400K lines of Elm in production, and have been using it since 2015. We've been super happy with it, and have no interest in going back to JS or TS.
(To be totally honest, at this point I can't imagine even a hypothetical JS/TS framework that could bring enough benefits for us as a business to justify giving up Elm's compiler and package ecosystem. I'd be super impressed if someone managed that though!)
It's ready for production. We've been using it for years without issues or bugs. It works as it is right now, and has done for years. It's not like it would stop working out of the blue.
Unless the benevolent dictator pushes out a new Elm version that is not backward compatible.
And, if the process is the same it was a few years ago, it could happen next month and only a happy few know it. The development is done behind closed doors.
It happened to a project of mine, from version 0.17 to 0.18 IIRC. The choice was between letting the code slowly rot, rewriting a large part of it still in Elm but with the new paradigm, or switching to some stable JS framework. I had overall enjoyed working with Elm until that.
Elm may not be a good fit for every project out there, like when you need tight integrations with specific JS projects, but it will work well for most projects.
At this point, I'd personally be more interested in hearing about cases where Elm was NOT a good fit (and why) rather than where it is.
Even if the creator died or lost interest you would be still fine to use it. The community could still provide critical patches if their was a need. Feature development might slow down but if it is already offering everything you need that is not really an issue, no?
What is the criterion for battle tested?
The other big Lisp nirvana thing is macros, and let's face it, they're cool, but the best way to use macros is sparingly, especially when you're working with other people, so they don't make as fundamental a difference to the way you code as you would expect. At least in my experience; YMMV.
If you're coming from modern Javascript and looking for a big idea that will change how you go about the job of programming, I think working with a static type system will be a bigger eye-opener.
My anecdote is that I wrote a backend Slack bot in Clojure (not ClojureScript) - the experience was great when writing it. But three months later I had to go fix something in this codebase and by then I didn't remember as much about it. And getting back "up to date" was so hard for me that I burned out on that project and rewrote it in Elm. (Yes you can write headless apps running on Node in Elm.)
The development experiences might be similar (Clojure might make you feel slightly smarter and cooler), but the maintainability "3 months after" was much better after the rewrite to Elm, since the compiler won't let you even run your app unless everything checks out (compared to Clojure which will just happily raise a runtime exception). And you have type annotations as a form of documentation for your future self.
Meanwhile writing a web app with Clojurescript will require making more choices around libraries you want to pull in, although you can always just go with re-frame which is awesome.
As far as more theoretical learning, I'd go with Clojurescript. Clojurescript is a real lisp with all the fun stuff you can do there. Elm is an intentionally gimped ML variant made so to make it more palatable to the Javascript developer community. If your goal is to learn about typed FP while running in the browser, you'd be better off with ReasonML.
I learned way more from Elm in terms of frontend architecture ( I am employed as frontender) then from any other framework/ language.
Whereas CLJS is at the other end. A tool for experienced developers who know what they want and how to do it. As it requires you to make a lot of choices. That Elm, for most parts, already made for you.
Main Clojurescript pro was re-frame[0]. Re-frame makes state handling easy and, especially importantly for performance-sensitive projects, it entirely removes the (rather common) problem of re-renders with the way its subscriptions are handled. This, plus the ability to have your entire stack in a single (amazing) language, were the tipping points to me.
While you can't achieve the same level of compile-time safety in Clojurescript, you can apply "state checks" (with Spec or any custom validation mechanism) any time your state changes. And with re-frame, it is extremely clear when your state changes, therefore you can block any invalid state from being saved and catching it as soon as it happens. Again, not the same kind of safety you have with Elm, but to me it's a good-enough trade-off.
And that's exactly what I do with my code: every time my state changes, I run a validation check on the keys that are about to change. If their value is deemed invalid, an error popup (identical to old Windows XP error popups, because why not) will appear, stating what the invalid state is and what it should be like instead, as well as which event led to this state in the first place. This check is so fast I don't even need to disable it in the production build (but instead of a popup we get a notification that an invalid state error happened).
In fact, there's this (non-mainstream) argument that runtime checks bring even more power over typing: https://www.youtube.com/watch?v=nqY4nUMfus8
TL;DR: Not an easy decision, but the truth is whichever one you pick, it will be a great choice.
Do both, but do Lisp first.
I have read all of the bad experience posts on Elm because I've spent a while working with it now and it is always valuable to see someone else's perspective on it. In practice the pain points either haven't really been a problem for me, or I've found a satisfactory way around them using web components, ports, code generation, or other ways.
My experience is just more anec-data and I know it won't represent everyone's experience. I'd encourage anyone hesitant about Elm to give it a go, it's been a joy to work with.
Python has real integers that don't overflow. Elm does not.
[0] https://discourse.elm-lang.org/t/where-can-we-find-the-roadm... [1] https://package.elm-lang.org/packages/elm/core/latest/Basics...
What was something that left you unsatisfied with Elm? The biggest one for me until relatively recently was handling state updating state from leaf components, it was fairly cumbersome to have `(Model, Cmd, ExternalMsg)` in lots of places. At work we recently switched to the Effect pattern (https://sporto.github.io/elm-patterns/architecture/effects.h...) which has alleviated a lot of the overhead.
Another side of this argument is that the rate of Elm ecosystem change is far more manageable than for JS.
A "stable JS framework" is an oxymoron, I'm afraid. My JS tools and NPM packages continuously change from under me, but somehow that's considered normal and not a problem.
I've just looked up a project I have with a mere 9 dependencies, and 7 of them have gone through multiple major version changes since the end of 2019. The other two have had minor releases. None are unchanged, even though all were already "stable" when I added them! Will the code still work if I update them? I don't know. Do I want to spend the time continuously tracking the changes to these dependencies, testing, making new releases of my project? Not really.
Perhaps I could continue deferring dependency updates, but that might make my job that much more painful when I have a legitimate reason to update (eg. for some new features I need) as half the package APIs will have changed beyond recognition by then.
I have so much less trouble with going back to Elm projects and picking up where I left off. That should be taken into consideration too.
That wouldn't break your build/deploy out of the blue, as you don't get force updated to the latest version.
And having worked on the 0.19 transition, it was in public preview for about 3/4 of a year and regularly discussed in the elm slack. When 0.19.1 came about, there were some more pre-release versions, where evan asked people to test and give feedback in slack. Can't speak for versions before that.
> if we managed to program in a "multidimensional-state paradigm", where you could call a "multidimensional f(x)" where in one dimension of the state it will always be a valid reply, and the side effects would be another f(x) that would solve the side-effects in parallel, but both functions would be intrinsically bounded to each other like in the schrodinger's cat experiment.
You're _kind of_ talking about monadic binding here, which is called the `andThen` pattern in Elm. You can see it in JS when handling promises too.
Ok, we can do all of this now, but in this vision, you would have some sort of Ohad Rodeh btree, which would track all the states (it basically needs to be some sort of GC and handle all the state allocations).
With this btree you would be able to create branches of the whole universe of states in a memory cheap way. Once theres a possible state change a new branch is created, and once consolidated(not just one, but it might change many states of the tree) it would be passed as the "front state".
If a side-effect prevented the change in state from occurring nothing would happen as the original state would keep the same.
I know this sounds trouble and that we kind of have the tools for this, but i've noticed that we have a "fractured state" problem where we end up doing much more work that it would be not actually needed if we managed to pack it all in a ubiquitous initial signal that would get 'sliced' into other signals with transforms, but would be consolidated into one again.
Rendering got that right, with a back buffer with a bunch of intervening state that is flipped as front once its all set. If things are breaking in the back buffer, just try it again or fail the whole state tree all at once, keeping the last good state intact.
We already do this manually in code, but we manage a lot of fractured states giving we don't have the bird-eye view perspective.
If you had something managing it all, looking at diffs, etc, my intuition(maybe wrong) says that it would make a more efficient and stable program, more performant, with more memory efficiency and easier to reason about from the developer perspective.
There are structs/records in like every FP language. The advantage of FP is that you can have the error code and description OR you can have the OK state value, but you can't have both in the same scope and the compiler guides you to that. And that rocks.
I had to come back to this because this kind of in-band signaling is how I've introduced bugs many-a-time in my old life. In FP it's so cheap to just make this function that can fail in some way return a wrapped up value that tells you what failed and how and _prevents_ you from doing math with what should be an error code (for instance).
What a cruel thing to make up and then say.
Just fuck off with that kind of comment. I provided a well reasoned post, dismissing my views like that just because they don't align with yours is dishonest on your part and no way to have a discussion.
To answer your claims: No, I couldn't easily say the same about TS/react. In elm the language forces the architecture and safety. In react it can be whatever you want with leaks and unsafe modifications all over the place.
Is just about the most ridiculous thing I ever heard (and certainly what a "true believer" would say even if you obviously and predictably disagree)
That a true believer will react angry to criticism is entirely self evident.
There are thousands of times more developers and companies using typescript over Elm, there is inherent value in this simple fact. No matter how much steam escapes your ears, its true.
Refactoring code or updating dependencies is and feels a lot safer in Elm than in TS, and doesn't require asking for every npm package author to add TS type definitions.
I'd also argue that TS/JS have the esoteric/cool features. Elm is a very simple language, complete enough to be able to write most programs, but small enough to give you a lot of guarantees about how the code will behave.
This is one way to mitigate those escape hatches:
https://github.com/cyrilletuzi/typescript-strictly-typed
But in my experience, you had better be starting a new project. They aren't joking when they say laying it on an existing codebase is a nightmare.
Having built extensively in both Elm and TypeScript, I'd disagree, there's no comparison, even after importing Elm-ish concepts like discriminated unions, functional purity, decoders and immutability into my TypeScript approach. As others mention, TS leaves some type ambiguity.
A badly-written Elm app can still be reliable, even if it ends up slow or sometimes acts funny. A badly written TS app is completely inscrutable.