Yes. The deeply nested components thing isn’t an issue at all. You build “components” as stateless views. It’s fundamentally the same as building an application out of all stateless components in redux.
Your model (the central state) becomes larger and more complex, but the size of your update function doesn’t expand as much as you’d think.
Meanwhile, the benefits from type-safety, pure functions, and extremely well-designed libraries like elm/parser & elm-UI are really terrific.
I think “deeply nesting components” means dropping a stateful component (view+behaviour+state) anywhere in your app without anything else changing. That’s not possible in Elm since the only place to store state is the central state storage. Which makes perfect sense, but often freaks people out, since it’s an uncommon design constraint.
With only one store, how do you keep your state reasonably organized when you get up to hundreds of pieces of data to manage?
And what do you do about generic reusuable components that need state? Say a typeahead search that needs to track the input string and the list of results from the server?
You can think of each root-level branch of the state tree as its own "model". In redux (I haven't used elm much) I implement generic components by finding a property that can be used to derive a unique key and creating an entry for each component in the reducer.
If you really really really can’t live without a statefull component, you can always build a custom element (which can be a wrapper around an elm app). But reusable views with fairly complex state are used; they require a little bit of wiring up, but it means you know exactly where to look when there’s a big/problem.
The official Elm Architecture tutorial used to contain some examples of encapsulating functionality into modules that would then be reused. The pattern presented there was abused by some folks and some problems that were introduced by state partitioning started to appear. There was a lot of talk about boilerplate and inter-component communication. A lot of these problems could have been avoided by not being so aggressive with the partitioning and so, "thinking in terms of components" has been discouraged ever since.
You can still define components by implementing the model/update/view triplet pattern but the main recommendation is to do it only when this is unavoidable (e.g. Pages or highly complex widgets).
A strictly typed pure functional language that generates JavaScript. That, along with a clever compiler, makes them claim that apps built with Elm don't have runtime exceptions.
Adding to others, with disclaimer that I haven't built a large application yet so these are relative newbie comments:
1. An amazingly helpful compiler. The best I've come across for identifying problems, describing them in a meaningful way, and suggesting how to fix.
2. A wonderful synergy between language and architecture. Various others have copied Elm's model-view-update architecture (React, F# SAFE) but as they're libraries, they don't feel quite as well-integrated.
3. An excellent, helpful community.
4. A BDFL in Evan who is focused on considered design and careful evolution over "move fast and break things". Some will (rightly) challenge if this is good: Elm 19 broke a lot in 18. For me personally, I much prefer Elm's thoughtful, well-considered evolution over the volatility in the wider javascript ecosystem
5. Some nice libraries. Elm-ui, for example, provides a different way to do lay out (no CSS). Again that's not for everyone - but useful if you're not a CSS guru.
6. Strong static typing with powerful type inference.
There are definitely downsides. The full-on, no-compromises immutability means there's some overhead in dealing with e.g. http request/responses or js interop. But there's corresponding value in the resulting guarantees in the code.
As a newbie I'm really taken with Elm; it's a refreshingly stable and integrated enclave in the otherwise volatile front end space. YMMV of course
I don't use it myself, but I was recently at Oslo Elm Day, where a consultant at The Norwegian State Railways shared his experiences with it (which were mostly good): https://youtu.be/yE9PKFI19RM
If your question is whether Elm is mature enough for production, the answer is yes. There are some companies out there, but don't let their opinion condition your experience.
Rather than relying on other people's opinion a better experience for you would be to port one sub-component of your existing system to elm and see how it goes from there, while you gain trust and experience in the new system.
Much stronger type guarantees. TypeScript's strength and weakness is how it fits JavaScript like a glove. Elm is a different language entirely, so it can provide much better soundness. It's also a simpler language than JS / TS. TypeScript, being a superset of JS, has a lot of baggage. Elm is pretty small and conceptually simple. Elm is also a framework, not just a language, so it guides your team into building things in one, consistent way (for better or for worse). Finally, the Elm compiler is very good. It produces small, fast build artifacts. Compare the bundle sizes of Elm with the other competing frameworks here[0].
You can do way less in Elm than you can do in TypeScript. Counterintuitively this makes your code much more robust.
Elm is far more opinionated. There is only one framework you can use. The type system is much stronger with no escape hatches. It is impossible to mutate variables. Interfacing to javascript is much harder, and there is only one way to do it, and any results coming back from javascript have to be checked and handled to ensure it is as you expect.
You don't need to choose bunch of frameworks and spend hours getting them work together. Elm basically provides the perfect language and abstractions for building UI.
Can you, for any button in your interface, at any point in time, reliably calculate at runtime what your page's data model would look like, and if there were going to be any side effects, if you were to click on that button - but without clicking on it or invoking its callbacks (which could potentially change all kinds of things)? You can do that with Elm (more info here - https://lukeplant.me.uk/blog/posts/two-experiences-with-elm/ ), due to The Elm Architecture and everything being immutable.
(In theory you could do this in TypeScript, but it would be a lot of work and its reliability would depend on everyone coding everything a certain, very unnatural way, with no help from the compiler).
I'm using Elm in production, version 0.18, it is an extremely robust way to make front end code. You just don't have runtime issues.
However, like IBM folks (mentioned in another reply) who said in the their post they didn't know how they would upgrade to 0.19, I also don't know how I will be able to upgrade. Specifically, 0.19 adds some cripple-ware restrictions (you can't use native modules unless you are contributing to certain projects). So, if you want to use something like Intl - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... - not only is there no wrapper provided, the restrictions in 0.19 attempt to stop you from writing your own wrapper. Only the core team can do it, which requires them having both the expertise and motivation to do so. Plus Elm has essentially a closed-source development process. In fact, there is no 'process' for contributing, and the fact that there is no process is deliberate, as far as I can tell.
So for me, if I can't find a way around the restrictions, they may kill my ability to being able to keep my code nicely architectured (i.e. using The Elm Architecture). I may be forced to switch to something like ReasonML with bucklescript-tea - https://github.com/OvermindDL1/bucklescript-tea
I think most of the Intl api could work reasonably well with ports and custom elements. The formatters in particular, which make up most of the API, would probably work quite nicely as custom elements. It's really only string comparison that I can imagine needing ports for, outside of some particularly unusual use case.
We do https://www.mybrocante.fr, it's Elm almost exclusively save for legacy parts we dare not touch with a 10-foot pole, and it's the best dev experience I've ever had =)
About nested module, they are not necessary at all. Check "The life of a file" by Evan Czaplicki - create a new file if Model/Update gets too big, there is no need for upfront component partitioning.
Elm made me hate working with Javascript in my job, and the differente is brutal. I don't believe in silver bullet but this is the domain where Elm shines: frontend development. I love every aspect of it. Elm is a very simple language, generates very small assets with a really fast compiler, gives us guarantees of no runtime exceptions in production, easy refactoring (in JS it would be a nightmare), has a time travel debugger (that shows your application state in any given moment), nice compiler error messages, and an incredible architecture which inspired redux (TEA) but is much better and simpler. There are problems ofcourse, for example not every Web API is covered yet, but it's possible to use Js ports to communicate with Elm.
Just out of curiosity, how often do you use time travel debugging? I have redux dev tools and can use it on any of my apps, but I've never felt that it was more useful than the step debugger. The first time I set it up I thought "huh, cool" and then never used it again. There's also the issue of side effects like ajax requests that interact with the outside world.
I had the same problem with time-traveling in react/redux. Everything has to be in the store for it to work, and since it's hard to make reusable components using the store, some things often end up outside. Then time-traveling becomes buggy. But since it's impossible with side-effects in elm, you can always trust it there. I use it pretty often to jump between states. The UI is not as good as redux-devtools and as feature-rich, though, that part I miss.
It doesn't end up spamming requests the same way as in react, as there is no "componentDidMount" that can trigger side-effects. In elm it's just different states being rendered.
Ah sorry, I didn't mean side effects in lifecycle methods, I meant DOM events like click, etc. If you click a button that deletes a resource on your API, there is no time traveling because the outside world doesn't time travel along with your debugger (unfortunately). :)
You can isolate your side effects somewhere, but they still have to respond to actions/events/messages so if your time traveler dispatches actions you'll end up making duplicate requests.
Everytime. If anything goes wrong I just see the message (action in redux) and see the current state in that time, so I can understand what happened. In some cases it is more effecting than sending a log.
Ah, I see. I use a customized redux logger that shows the before/after state and the action payload. That is what I've always used for trying to diagnose bugs so I've never found a reason to use the time travel tools.
Although it's recommended to not build components with states, you could still do it if you think it's necessary for your app. I documented the codebase structure I used at; https://azer.bike/journal/elm#codebase-structure
I recently did a talk at Oslo Elm Day where I talked about our experience with Elm at my company after using it as the main web langauge for the past 18 months: https://youtu.be/yE9PKFI19RM
TL;DR:
- The experience has been terrific.
- Best compilator I have ever worked with.
- Mostly great tooling. Love the auto-formatter.
- Great libraries
- No major issues, some small itches but nothing that can't be worked around.
- No runtime errors in 18 months.
- Very maintainable code
- New team members quickly becomes productive in Elm, even with no prior experience (It helps to have someone with experience on the team to guide the learning and answer questions).
When it comes to nesting we sometimes make use of a component-like pattern, where we have modules with their own state, messages, update and view functions that are used by the main functions. To see an actual example of this check out this SPA example: https://github.com/rtfeldman/elm-spa-example/blob/master/src...
I’ve used it on a deployed and used project app. On a substantial commercial internal project. And have 2 other apps in the works.
I’ve used ports and custom elements to integrate JS code.
I’m convinced Elm is an excellent way (and my preferred way) to build frontend apps.
The learning curve is a bit tricky. I think the path to toy apps is straightforward and fun. The path to “real world” apps requires a bit more effort. Expect to do some homework, hang out in forums etc.
I've been picking it up lately and I'm curious if you have any links to non-trivial application sources. I'm curious how the architecture scales and how it handles cross cutting concerns.
While I'm glad to see that there are many positives for using Elm,
I'm curious about the "lessons learned" that come about from maintenance of the project.
I found Elm to be very productive. Custom types allow you to codify your business logic and _can_ make bogus application state impossible; the compiler's error messages are helpful and the steps to resolve the problem are (usually) obvious; the FLOSS libraries are excellent (elm-css, elm-test); the community is very supportive; JavaScript interop is safe and simple (mostly -- decoding can be a little confusing at first and could use more documentation and tutorials); the compiled JS is _small_ and the applications feel very snappy.
I didn't have any issues with nesting. Your views unfold to match your model. I found creating sub-models for each "page" within my app to be very intuitive -- these choices became obvious as I started prototyping -- and it's easy to share partials across different page views.
It's used in two applications at CityBase.
Given that I didn't write any of it, I can't answer the deeply nested component question.
Our experience: It works well. However, we will probably stick with React or something else going forward.
Using Elm for an admin interface (not web-facing).
Elm is incredibly reliable (that's why we experimented with it in the first place) as well as very productive once you get used to the Functional paradigm.
Refactoring especially is a breathe (as long as it compiles, it usually works) and we didn't need to test the frontend for it to stay sane.
We even started covering more and more of our admin's old views with it and are on our way towards an SPA.
Yes, replaced few rich and reasonably complex UI components in admin interface that were previously developed in Adobe Flash (developed in the times when html wasn't as expressive for more visual/interactive UIs). The end users did not notice any difference.
The language is pure joy to work with. No issues with "deeply nested components", just use a function to render a "component", add data needed for the rendering as a function parameter, handle the events emitted from the "component" in the update-function.
Am I missing something or are most submissions here mostly small landing pages and small form examples? Are there some more complicated CRUDdy showpieces?
We've been using Elm in production for the last year to power an app used by 100,000 public school students in the US, and it's been fantastic.
The Elm language and framework really do live up to their promise: it's a delightful development experience. Despite rewriting our entire 4-year-old UI we've had remarkably few bugs despite multiple large refactors. The whole team was new to Elm when we started, and now everyone's an advocate.
Re: components, I'd echo what others have said. Because the view code can't have side effects, components become much simpler and a whole category of the problems we've had in one of our Javascript apps just don't happen. You sometimes will want to separate out logic for subsystems in your update function, but that's pretty well supported.
Also, if you're interested in working in Elm and making a measurable difference in student learning, we're hiring!
Also also, if you're interested in meeting the Elm community in person, we've just announced the speaker lineup for Elm in the Spring, a new 1-day conference in Chicago in April -- we're really excited :D :D :D
I was part of the team that built an editorial admin panel for the most read news site in my country. The initial development phase ended around three years ago and it's been in a sort of continuous development mode since then. A whole bunch of people have added features to the codebase (and upgraded it from 0.16 to 0.17 and 0.18) after me and the other original devs left.
It is still going strong and is so reliable that there's no need for dedicated developer for that project. I honestly can't imagine any sort of JavaScript project could have survived through the years that well.
73 comments
[ 3.1 ms ] story [ 143 ms ] threadYour model (the central state) becomes larger and more complex, but the size of your update function doesn’t expand as much as you’d think.
Meanwhile, the benefits from type-safety, pure functions, and extremely well-designed libraries like elm/parser & elm-UI are really terrific.
And what do you do about generic reusuable components that need state? Say a typeahead search that needs to track the input string and the list of results from the server?
A normal practice is to have a model per page. For e.g. https://github.com/rtfeldman/elm-spa-example/blob/master/src...
And you can have another item next to it for global state if you need to. https://github.com/ohanhi/elm-shared-state explains one way to do this
The official Elm Architecture tutorial used to contain some examples of encapsulating functionality into modules that would then be reused. The pattern presented there was abused by some folks and some problems that were introduced by state partitioning started to appear. There was a lot of talk about boilerplate and inter-component communication. A lot of these problems could have been avoided by not being so aggressive with the partitioning and so, "thinking in terms of components" has been discouraged ever since.
You can still define components by implementing the model/update/view triplet pattern but the main recommendation is to do it only when this is unavoidable (e.g. Pages or highly complex widgets).
1. An amazingly helpful compiler. The best I've come across for identifying problems, describing them in a meaningful way, and suggesting how to fix.
2. A wonderful synergy between language and architecture. Various others have copied Elm's model-view-update architecture (React, F# SAFE) but as they're libraries, they don't feel quite as well-integrated.
3. An excellent, helpful community.
4. A BDFL in Evan who is focused on considered design and careful evolution over "move fast and break things". Some will (rightly) challenge if this is good: Elm 19 broke a lot in 18. For me personally, I much prefer Elm's thoughtful, well-considered evolution over the volatility in the wider javascript ecosystem
5. Some nice libraries. Elm-ui, for example, provides a different way to do lay out (no CSS). Again that's not for everyone - but useful if you're not a CSS guru.
6. Strong static typing with powerful type inference.
There are definitely downsides. The full-on, no-compromises immutability means there's some overhead in dealing with e.g. http request/responses or js interop. But there's corresponding value in the resulting guarantees in the code.
As a newbie I'm really taken with Elm; it's a refreshingly stable and integrated enclave in the otherwise volatile front end space. YMMV of course
Rather than relying on other people's opinion a better experience for you would be to port one sub-component of your existing system to elm and see how it goes from there, while you gain trust and experience in the new system.
[0] https://github.com/gothinkster/realworld
Elm is far more opinionated. There is only one framework you can use. The type system is much stronger with no escape hatches. It is impossible to mutate variables. Interfacing to javascript is much harder, and there is only one way to do it, and any results coming back from javascript have to be checked and handled to ensure it is as you expect.
The redux-like (well, more like redux is elm-like) is baked into the type system so you know it will be done "right".
And thanks to the above, there's less bikeshedding within the team from people who want to reduce boilerplate for bad reasons and make the app worse.
Can you, for any button in your interface, at any point in time, reliably calculate at runtime what your page's data model would look like, and if there were going to be any side effects, if you were to click on that button - but without clicking on it or invoking its callbacks (which could potentially change all kinds of things)? You can do that with Elm (more info here - https://lukeplant.me.uk/blog/posts/two-experiences-with-elm/ ), due to The Elm Architecture and everything being immutable.
(In theory you could do this in TypeScript, but it would be a lot of work and its reliability would depend on everyone coding everything a certain, very unnatural way, with no help from the compiler).
I'm using Elm in production, version 0.18, it is an extremely robust way to make front end code. You just don't have runtime issues.
However, like IBM folks (mentioned in another reply) who said in the their post they didn't know how they would upgrade to 0.19, I also don't know how I will be able to upgrade. Specifically, 0.19 adds some cripple-ware restrictions (you can't use native modules unless you are contributing to certain projects). So, if you want to use something like Intl - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... - not only is there no wrapper provided, the restrictions in 0.19 attempt to stop you from writing your own wrapper. Only the core team can do it, which requires them having both the expertise and motivation to do so. Plus Elm has essentially a closed-source development process. In fact, there is no 'process' for contributing, and the fact that there is no process is deliberate, as far as I can tell.
So for me, if I can't find a way around the restrictions, they may kill my ability to being able to keep my code nicely architectured (i.e. using The Elm Architecture). I may be forced to switch to something like ReasonML with bucklescript-tea - https://github.com/OvermindDL1/bucklescript-tea
About nested module, they are not necessary at all. Check "The life of a file" by Evan Czaplicki - create a new file if Model/Update gets too big, there is no need for upfront component partitioning.
IBM App with Elm for most of the front-end: https://discourse.elm-lang.org/t/ibm-releases-elm-powered-ap...
[Disclosure: I'm an IBMer - but not involved on this project]
Most recent example: https://www.pavoq.com a SPA built with Elm and Rust
You can isolate your side effects somewhere, but they still have to respond to actions/events/messages so if your time traveler dispatches actions you'll end up making duplicate requests.
Although it's recommended to not build components with states, you could still do it if you think it's necessary for your app. I documented the codebase structure I used at; https://azer.bike/journal/elm#codebase-structure
TL;DR: - The experience has been terrific. - Best compilator I have ever worked with. - Mostly great tooling. Love the auto-formatter. - Great libraries - No major issues, some small itches but nothing that can't be worked around. - No runtime errors in 18 months. - Very maintainable code - New team members quickly becomes productive in Elm, even with no prior experience (It helps to have someone with experience on the team to guide the learning and answer questions).
When it comes to nesting we sometimes make use of a component-like pattern, where we have modules with their own state, messages, update and view functions that are used by the main functions. To see an actual example of this check out this SPA example: https://github.com/rtfeldman/elm-spa-example/blob/master/src...
I’ve used ports and custom elements to integrate JS code.
I’m convinced Elm is an excellent way (and my preferred way) to build frontend apps.
The learning curve is a bit tricky. I think the path to toy apps is straightforward and fun. The path to “real world” apps requires a bit more effort. Expect to do some homework, hang out in forums etc.
Also, we're hiring! No Elm experience required; over the years we've had many new hires pick it up on the job. :)
https://www.noredink.com/jobs
I found Elm to be very productive. Custom types allow you to codify your business logic and _can_ make bogus application state impossible; the compiler's error messages are helpful and the steps to resolve the problem are (usually) obvious; the FLOSS libraries are excellent (elm-css, elm-test); the community is very supportive; JavaScript interop is safe and simple (mostly -- decoding can be a little confusing at first and could use more documentation and tutorials); the compiled JS is _small_ and the applications feel very snappy.
I didn't have any issues with nesting. Your views unfold to match your model. I found creating sub-models for each "page" within my app to be very intuitive -- these choices became obvious as I started prototyping -- and it's easy to share partials across different page views.
What are the reasons you wouldn't consider using it going forward?
Elm is incredibly reliable (that's why we experimented with it in the first place) as well as very productive once you get used to the Functional paradigm.
Refactoring especially is a breathe (as long as it compiles, it usually works) and we didn't need to test the frontend for it to stay sane.
We even started covering more and more of our admin's old views with it and are on our way towards an SPA.
The language is pure joy to work with. No issues with "deeply nested components", just use a function to render a "component", add data needed for the rendering as a function parameter, handle the events emitted from the "component" in the update-function.
The Elm language and framework really do live up to their promise: it's a delightful development experience. Despite rewriting our entire 4-year-old UI we've had remarkably few bugs despite multiple large refactors. The whole team was new to Elm when we started, and now everyone's an advocate.
Re: components, I'd echo what others have said. Because the view code can't have side effects, components become much simpler and a whole category of the problems we've had in one of our Javascript apps just don't happen. You sometimes will want to separate out logic for subsystems in your update function, but that's pretty well supported.
Also, if you're interested in working in Elm and making a measurable difference in student learning, we're hiring!
https://www.esparklearning.com/company/careers
https://www.elminthespring.org/
It is still going strong and is so reliable that there's no need for dedicated developer for that project. I honestly can't imagine any sort of JavaScript project could have survived through the years that well.