I posted about our team's decision to start using Elm to develop front-end applications. That post deals with the high level benefits of using the language, but what does it really mean to be a developer working in this language?
Elm is a functional language, which means you'll run into a few concepts that you have to tackle, but Elm as a community is also very intentional about being mainstream-friendly, so if that word functional sounds too fancy or weird or difficult or niche, I'd urge you to give it a try. You will have to change the way you think about some things, but you'll do it with helpful tutorials, documentation, and community.
Elm has a small set of features which you can learn quickly, and because its feature set is constrained, it tends to guide you in the direction of writing clean, well-architected code. The Elm community prefers to have one way to do things, so you're not left guessing.
In my opinion, the big concept that you have to wrap your head around in order to be productive in Elm is immutability. Elm enforces immutability, which means that instead of changing existing values you return new ones. Because nothing ever changes after it's created, you have some wonderful guarantees when reasoning about your program, as well as guarantees that the language can take advantage of to optimize your program in interesting ways. However, if you're used to using loops (with a counter that changes) or poking into shared global state, you'll have to learn new techniques. However, much of time you may find that you're already familiar with these techniques, because many mainstream languages have been borrowing functional concepts over the past few years. For example, you'll replace that loop with something that looks very much like LINQ.
As a quick example, if you want the cost of all green apples, it might look something like this:
|> List.filter (\apple -> apple.color == Green)
|> List.map (\apple -> apple.price)
However, there are places where what you want to do isn't already wrapped up in a function and you'll have to dust off your Comp Sci 101 book and do some recursion.
Elm is a statically typed language, which is one of its great strengths. This means that when you try to pass a
String to a function that expects an
Int, it will tell you that you can't do that. However, the compiler is designed to be an assistant, not an adversary, and has best-in-class error messages for when your code is trying to do the wrong thing. It's much better to find that out at compile time than at run time. The common refrain that you hear from Elm developers is that once you satisfy the compiler, your code just works (i.e. if it compiles, the only thing that can go wrong is bad business logic).
Elm has type inference, meaning that you don't have to label your types if you don't want to, Elm will figure out the types your code requires and make sure everything matches (however, you can annotate your functions with types to clarify your intention and constrain the types you allow). This removes a lot of the ceremony that is often the reason developers want to avoid typed languages such as Java and C#. I have yet to run into a scenario in Elm where the compiler wouldn't let me do something with my code that I want to be able to do (which is a common scenario in C#'s type system).
Elm comes with a package manager, so that you can install other Elm libraries easily. The package repository requires adherence to SemVer, meaning that it requires a major version number change when it detects breaking changes in your module's API. This means you can be sure that your code won't break when updating to a new version of a module. Additionally, it requires that published modules start at version 1.0.0 (as opposed to npm, where the vast majority of packages are version 0.x), implying a level of maturity of the code that's been published to the repository.