I didn't mean that only Javascript developers would be lost, more that Elm kind of "does its own thing" and as such some of its errors or mechanics can be a bit cryptic and require some more experience with the language.
It's not a language you can pick up and go as easily as Javascript is more what I meant, in my opinion at least unless, you're already familiar with pure and strictly typed languages.
I don't think Elm does its own thing. The ideas used in Elm are more "discovered" than "invented" (at least relative to JavaScript), if you're familiar with that line of thinking.
I also don't think its errors are cryptic. Elm's error messages are best in class.
> It's not a language you can pick up and go as easily as Javascript
I don't agree with this either. After having taught people Elm, and also JavaScript as a first language, I don't think mutability is more intuitive to a beginner.
Oooh that's a very hard question :p I'd need to sit down a bit to answer it, to be honest it could almost be its own article. They both try to accomplish the same things through very different means with very different backings (Facebook versus literally one person).
I can already tell you that ReasonML definitely has my preference these days over Elm, for the simple fact that I can keep using React with it. I love React, it's one of my favorite frameworks. If you're already familiar with it you're 50% of the way there to learn ReasonReact, whereas with Elm it really lives in its own little universe that you need to get familiar with from scratch.
I find ReasonReact's underbelly a bit messier, and the compiler is not as good nor as clear as Elm's though. And the whole OCaml layer brings some chaos too, like the multiple standard libraries available in ReasonML (OCaml's, ReasonML's, etc) which can all be very confusing when you begin.
So, I wouldn't really know what to tell you, it depends wildly of your experience, what you like, what you're looking for in the language and so on. I'd definitely recommend to give both a shot and see what you like as they're both great languages to me, even if ReasonML has my preference.
Indeed sorry that's badly worded, both compilers are very good, I meant more the UX around compilation in general as I'm not apt to judge the compilers themselves.
To me the biggest difference is that [Reason]React focuses on components which encapsulate state, while Elm focuses on data structures and pure stateless functions. Both approaches have pros and cons. You can design a [Reason]React app similar to an Elm app, if you use pure stateless components exclusively and handle your state with few reducers, but you won't get the same smooth experience which you get with Elm. I still favor React though. React's component interface adds some friction and complexity, but it's also a nice abstraction which helps a lot with code sharing, especially when sharing with third parties. Thus the striving ecosystem (UI frameworks, UI builders, etc.) in React.
I made a round trip over the last years from React/TS to Elm to ReasonReact back to React/TS. React/TS is the least attractive, but it's the one I get most done with. Plus the situation in React has gotten much better since Hooks, PureComponents, Context API. I'm glad the mess with class components is finally over.
For anyone new trying to get into Elm (as a language/tool), I would look elsewhere. ReasonML, PureScript, TypeScipt, etc are all better featured solutions. Having said that, the article is well written and thorough for anyone wanting to take a look into Elm.
But there are massive problems with Elm itself which make it impossible to consider seriously. I have and still do use Elm in production, and went through the nightmare upgrade from 0.18 to 0.19.
1. Elm ports are not sound. Elm will tell you everything is good but doesn't do simple checks - as say PureScript does - to see if your port code actually exists or adheres to the defined contract. There are other tree-shaking issues, but Elm does not know and does not care about the js you've written.
2. Evan holds the naive view that everything can be rewritten in pure Elm and there's no reason not to. An overwhelmingly common use-case is 3rd party services or tracking libraries where it's impossible to re-implement; these apparently don't exist.
3. Elm applications, without a Redux-style (domain-specific to Translator) pattern, end up being horrendous spaghetti code of everything knowing about everything. Cmds and Tasks are a pain to work with when they should be interoperable in some manner.
For small embeddable or personal projects Elm is a fun tool. I don't see it being more than that until more community involvement is permitted - removing the dictatorship.
For what it's worth, PureScript is much more complex than Elm. I see people recommend it, but I don't see people actually use it nearly as much as Elm (not that Elm is super widely used, of course). I do agree that the biggest thing Elm needs is to open up development, break down the "dictatorship" as you put it, and accelerate the pace rapidly.
I think OCaml/ReasonML seems like the nicest choice, even if you're using the Elm architecture. Functors make it really easy to combine components, which actually makes it nicer than Elm for the same tasks.
> Evan holds the naive view that everything can be rewritten in pure Elm and there's no reason not to. An overwhelmingly common use-case is 3rd party services or tracking libraries where it's impossible to re-implement; these apparently don't exist.
He quite loudly holds the opposite view: instead of thinking you need to port everything to Elm (very common urge for newcomers), keep it in JS and use it over a port.
All the wording in https://discourse.elm-lang.org/t/native-code-in-0-19/826 seems to indicate the opposite. I'm not saying jQuery bindings are a good idea, but saying no to _everything_ b/c you don't want jQuery bindings is.
Ha, OP meant directly? Well AFAIK that is simply not possible, to keep the purity of Elm programs. All has to go through the Elm architecture. Or am I mistaken?
You mean ports and flags? It's described right in the tutorial : https://guide.elm-lang.org/interop/ports.html.
I wouldn't call it not possible. It is necessary to have the cool stuff of Elm, unfortunately (or not).
We have a large Elm app and do the occasional bit of JS interop when required, it's not a problem at all.
The 0.19 change simply limited the ability to publish packages containing interop to the official Elm package repository. You can still host your own packages etc.
I am a back-end developer, and I don't know front-end.
I also know very little of functional programming.
Elm is the only language that I like writing, and where I don't feel in a state of frustration when I write.
It might have a lot of limitations, bu tit makes me happy. Given that our job is basically to be in a constant state of half broken code, this makes me a huge fan of the language.
our job is basically to be in a constant state of half broken code
In my experience, Elm code is only ever in a state of "horribly broken, won't compile" or "works". Whether that's a bug or a feature depends on what I'm trying to do...
Yes, because in elm everything is pure. At least in the code you write. Elm will force you to declare all possible cases that can happen, all branches in every function you write. That leaves literally nothing to break.
Not saying you can't break an elm program. But you won't run into runtime exceptions in elm, by design:)
Elm makes no claims to correct your logic. Logical errors are not caught by the compiler. You might still decode a JSON field to the wrong field on a Record (if the fields are the same type), you might invert a BOOL and use True were you meant False, you might incorrectly calculate some value, etc.
But, state errors are caught – your application can not get into an impossible state and therefore should never have any runtime errors.
Getting back to logical errors – Elm can provide some additional safeguards. For example you could have a record
``` type alias Point = { x: Int, y: Int } ```
and then decode it with
``` decode.map2 Point (field "y" int) (field "x" int) ```
Whoops you got the wrong order.
So you could make X and Y explicit types:
```
type alias Point = { x: PointX, y: PointY }
type PointX = PointX Int
type PointY = PointY Int
decode.map2 Point (field "x" decodeX) (field "y" decodeY)
```
Now your decoder won't compile unless the types line up correctly. But this adds a bit of extra overhead in unwrapping the values, but not much.
Neither Elm, nor any language in the Typed FP family, can do that. What it can do is to prevent us from making clerical mistakes. That's the surprising thing I learned programming in Typed FP: we very rarely make logical mistakes. Our programs are all broken, make no mistake, but they are broken not because we accidentally swapped a conditional (logical mistake), but rather because we passed in data of the wrong shape (clerical mistake).
A common clerical mistake which Elm, Reason etc. prevents, is when we grab something from a list and pass it to another function. What if in a rare case it is possible that the element doesn't exist? A function deeper in the stack might crash, or if defensively built, substitute it with an empty value. Both could be wrong.
By providing more information about our data and our functions to the compiler, the compiler can ensure our code always follow those constraints. These constraints are often in our head when using dynamically typed languages, or even statically typed OO languages. Typed FP helps us make them explicit in code so the compiler can track it instead of we tiring ourselves mentally and making a mess of it.
> These constraints are often in our head when using dynamically typed languages, or even statically typed OO languages.
I’m curious about this comment on statically typed OO languages. I work mainly with C# and don’t have much FP experience. Are you referring to exhaustive matching?
Edit: To clarify, I understand that C# compiler won’t prevent you from trying to access an element in an empty collection. What prevents you, in a typed FP language, from accessing head of an empty list?
Yes I was thinking of a couple of things, but most importantly the existence of null in the language. So functions either receive exactly the type they were looking for (which is better than dynamically typed), or a null (which never happens in a Typed FP language).
In OCaml, (List.hd []) throws an exception, which is not a desirably behaviour, so we mostly use a safer version of List.hd which would return an option type. This could be either "None" or "Some(value)". To operate on option types, we have to pattern match and handle both cases where the value could be either None or Some)
Exhaustive pattern matching is useful here, so is the very notion of variants. Without variants (or sum type / union type) we tend to leave domain concepts implicit in the codebase. While we can simulate variants with classes, it is too unwieldy to be used except for core domain concepts.
Expanding slightly, Elm (as well as others in this family), can create structures that assert certain rules. In the case of a list being empty, you can also use ADTs to create nonempty version ala `type NonemptyList a = NonemptyList a (List a)` where it's impossible to construct without that first element even if the second list is empty. A lot of languages lack this sort of feature. Then you can go about your code knowing you neither have to handle empty nor write a bunch of tests because empty construction is impossible and this is enforced by the compiler.
Sometimes I wonder whether Alan Perlis would mock at us for being Bible salesmen for Typed FP languages.. yet it is such a quality-of-life upgrade and a thinking tool that it feels justified to shout from the rooftops :)
And if you do what is your impression? I created it because I was frustrated with Elm (more the community than the language itself although the language has some issues as well).
I love Elm, but this tutorial is based on Elm 0.18, and 0.19 is out. There are some straighforwared but significant changes with how an application is set up.
If you're just starting out, find a 0.19 tutorial.
Why exactly? Elm 0.19 is out on August 2018, and the only thing that's expected is 0.19.1 which barely changes anything (doesn't break any api). Seems like 0.19 is quite settled at this point.
Will my Elm code written today still be maintainable with an up-to-date elm environment in 3 years? It certainly isn't the case with code written 3 years ago.
Doubtful, but possible. What is highly likely is there will be 1 or 2 upgrades between now and in 3 years. If history is any indicator these upgrades will be relatively painless.
The next upgrade, 0.19.1, will be completely backwards compatible.
By contrast, I have a 12K-line Clojurescript project from 6 years ago that interfaces with React and still compiles and runs beautifully, even if I update the Clojure language version to the current version.
Ok, I should have said 'one of the reasons' ... and maybe there is a better road-map, but I was dabbling with Elm around 0.16, and then watched 0.17 change a bunch of stuff I liked to bunch of stuff I didn't care about as much and I decided to stay away for a while.
But if it looks like we would be on 0.19.x for few years to come, I might dip in it again ... it's not like I ever published anything in Purescript or ReasonML and I never used Elm in anger, so it is probably just a matter of time til I jump in again :)
Yes, frustration of people going through previous versions is definitely a thing! Let's recall not only the infamous drop of JS FFI (for Ports), but also things like ditching custom operators. Let's see how >0.19 will go.
Yeah, I balked at changing my apps to be 0.19 compatible. Now I'm kind of just "getting along" with 0.18 until I can do more of my front end work in Phoenix LiveView.
I've been lurking Elm for a while, and with Elm 18 came to the conclusion that I should avoid it, because there was a single creator who simply could not keep up, and was uninterested in fostering a healthy community of contributors.
With Elm 19, I changed my mind. A closer following of the community shows that Evan is (a) nice, and (b) interested in contributions, but mostly in a researchy way. More importantly, Elm 19 seemed (to me) to tip the balance in usefulness and cleanliness.
And with the idea of taking Elm straight to WASM (and with WASI around), things are looking interesting in the (eventual… gosh but it moves slowly) future.
tl;dr: It's absolutely worth taking a new look at Elm 19.
In my experience this has not been a problem. First upgrades are very infrequent. Second, they occur for very good reasons. Third there is tooling to handle a lot of the code migration. Fourthly, the compiler will help you fix the rest. Lastly, each new version has been a real improvement on the previous.
Been working with Elm on so many projects the last 2-3 years.
I don't think any other js-based framework can reach the benefits that Elm brings.
Correct, readable and sane code at scale.
And, like others said, makes front-end coding enjoyable and confident.
JS is a scripting language, good for small and quick scripts.
Shouldn't be used for anything more than a few hundred LOC.
The biggest problem with elm in production right now is that there is no backward compatibility yet (version < 1.0). Code written in Elm 0.18 is not compatible with code written in Elm 0.19.
I like Elm as a language, but the community is extremely toxic, and it's not production-ready yet (version < 1.0, one-person project). Too bad, because it's a great project.
richard feldman, evan's colleague, is not exactly a nice person on the internet. There are several times where he's ranted or had a temper tantrum for no particularly good reason.
now he alone does not make up the "community", but he's certainly a prominent member of it.
I've seen people banned from official forums because they disagreed with design decisions. And several threads locked with messages like "oh yeah, haha, we don't really want you to talk about that" (https://discourse.elm-lang.org/t/elm-0-19-released-what-has-... for instance)
This is not FUD. Using it in production is a risky bet. And I say that as someon using Elm in production. We use it in a short-term, demo project. The project was started using version 0.18. Now we're stuck, either we rewrite huge parts of the app (and we don't have time for that), or we're stuck with older versions of the libs.
This is not something you can afford to do with a long-running, critical app, or at least you have to know you're taking some risks. It's a tradeoff.
Also, what would happen to Elm if the author suddenly died?
I've worked with many languages, beginning with Fortran in my student days that I had to punch my own cards, then doing Basic, Pascal, C, C++, Python, Ruby, Javascript, Lisp, Scheme, some Haskell, not necessarily in that order. I've never had more fun and felt more productive than with Elm. The ability to do "extreme refactoring" and come out on the other side with everything working is liberating, exhilarating, and makes code maintainable and growable over the long term.
83 comments
[ 1.7 ms ] story [ 163 ms ] threadI always found this to be a weird take. Elm is "Maybe Not As Good" because the average JavaScript developer isn't immediately familiar with it?
Maybe we should just be less averse to learning new things, and I don't think simple syntax swaps really count as new things.
Unrelated: your ToC doesn't work.
It's not a language you can pick up and go as easily as Javascript is more what I meant, in my opinion at least unless, you're already familiar with pure and strictly typed languages.
I also don't think its errors are cryptic. Elm's error messages are best in class.
> It's not a language you can pick up and go as easily as Javascript
I don't agree with this either. After having taught people Elm, and also JavaScript as a first language, I don't think mutability is more intuitive to a beginner.
I can already tell you that ReasonML definitely has my preference these days over Elm, for the simple fact that I can keep using React with it. I love React, it's one of my favorite frameworks. If you're already familiar with it you're 50% of the way there to learn ReasonReact, whereas with Elm it really lives in its own little universe that you need to get familiar with from scratch.
I find ReasonReact's underbelly a bit messier, and the compiler is not as good nor as clear as Elm's though. And the whole OCaml layer brings some chaos too, like the multiple standard libraries available in ReasonML (OCaml's, ReasonML's, etc) which can all be very confusing when you begin.
So, I wouldn't really know what to tell you, it depends wildly of your experience, what you like, what you're looking for in the language and so on. I'd definitely recommend to give both a shot and see what you like as they're both great languages to me, even if ReasonML has my preference.
in the article, you mentioned:
> the compiler is one of the best compilers I’ve ever seen and will not compile if it detects that your code is not sound.
It seems like your're judging the entirety of the compiler just on it's user displayed error messages?
I made a round trip over the last years from React/TS to Elm to ReasonReact back to React/TS. React/TS is the least attractive, but it's the one I get most done with. Plus the situation in React has gotten much better since Hooks, PureComponents, Context API. I'm glad the mess with class components is finally over.
But there are massive problems with Elm itself which make it impossible to consider seriously. I have and still do use Elm in production, and went through the nightmare upgrade from 0.18 to 0.19.
1. Elm ports are not sound. Elm will tell you everything is good but doesn't do simple checks - as say PureScript does - to see if your port code actually exists or adheres to the defined contract. There are other tree-shaking issues, but Elm does not know and does not care about the js you've written.
2. Evan holds the naive view that everything can be rewritten in pure Elm and there's no reason not to. An overwhelmingly common use-case is 3rd party services or tracking libraries where it's impossible to re-implement; these apparently don't exist.
3. Elm applications, without a Redux-style (domain-specific to Translator) pattern, end up being horrendous spaghetti code of everything knowing about everything. Cmds and Tasks are a pain to work with when they should be interoperable in some manner.
For small embeddable or personal projects Elm is a fun tool. I don't see it being more than that until more community involvement is permitted - removing the dictatorship.
He quite loudly holds the opposite view: instead of thinking you need to port everything to Elm (very common urge for newcomers), keep it in JS and use it over a port.
Elm sparks joy, similar to Ruby/Rails did back in the day <3.
It's not rocket science tough, and it allows for clean interfaces. I wrote about it just a couple ago ago : https://lengrand.fr/a-short-introduction-to-ports-and-flags-...
[0] formerly behind a review queue / review process to add to native-whitelist, now limited to two specific "core" organisations
The 0.19 change simply limited the ability to publish packages containing interop to the official Elm package repository. You can still host your own packages etc.
Asynchronous IO through ports works just fine.
But it's straightforward to interface JS from Elm. It's right in the tutorial under ports.
Elm is the only language that I like writing, and where I don't feel in a state of frustration when I write. It might have a lot of limitations, bu tit makes me happy. Given that our job is basically to be in a constant state of half broken code, this makes me a huge fan of the language.
Not saying you can't break an elm program. But you won't run into runtime exceptions in elm, by design:)
But, state errors are caught – your application can not get into an impossible state and therefore should never have any runtime errors.
Getting back to logical errors – Elm can provide some additional safeguards. For example you could have a record
``` type alias Point = { x: Int, y: Int } ```
and then decode it with
``` decode.map2 Point (field "y" int) (field "x" int) ```
Whoops you got the wrong order.
So you could make X and Y explicit types:
``` type alias Point = { x: PointX, y: PointY } type PointX = PointX Int type PointY = PointY Int decode.map2 Point (field "x" decodeX) (field "y" decodeY) ```
Now your decoder won't compile unless the types line up correctly. But this adds a bit of extra overhead in unwrapping the values, but not much.
A common clerical mistake which Elm, Reason etc. prevents, is when we grab something from a list and pass it to another function. What if in a rare case it is possible that the element doesn't exist? A function deeper in the stack might crash, or if defensively built, substitute it with an empty value. Both could be wrong.
By providing more information about our data and our functions to the compiler, the compiler can ensure our code always follow those constraints. These constraints are often in our head when using dynamically typed languages, or even statically typed OO languages. Typed FP helps us make them explicit in code so the compiler can track it instead of we tiring ourselves mentally and making a mess of it.
I’m curious about this comment on statically typed OO languages. I work mainly with C# and don’t have much FP experience. Are you referring to exhaustive matching?
Edit: To clarify, I understand that C# compiler won’t prevent you from trying to access an element in an empty collection. What prevents you, in a typed FP language, from accessing head of an empty list?
In OCaml, (List.hd []) throws an exception, which is not a desirably behaviour, so we mostly use a safer version of List.hd which would return an option type. This could be either "None" or "Some(value)". To operate on option types, we have to pattern match and handle both cases where the value could be either None or Some)
Exhaustive pattern matching is useful here, so is the very notion of variants. Without variants (or sum type / union type) we tend to leave domain concepts implicit in the codebase. While we can simulate variants with classes, it is too unwieldy to be used except for core domain concepts.
I didn't think so. So claims like:
> In my experience, Elm code is only ever in a state of "horribly broken, won't compile" or "works".
Which I find to be pretty common among fans of strongly typed functional languages, is incorrect.
And if you do what is your impression? I created it because I was frustrated with Elm (more the community than the language itself although the language has some issues as well).
If you're just starting out, find a 0.19 tutorial.
The next upgrade, 0.19.1, will be completely backwards compatible.
But if it looks like we would be on 0.19.x for few years to come, I might dip in it again ... it's not like I ever published anything in Purescript or ReasonML and I never used Elm in anger, so it is probably just a matter of time til I jump in again :)
With Elm 19, I changed my mind. A closer following of the community shows that Evan is (a) nice, and (b) interested in contributions, but mostly in a researchy way. More importantly, Elm 19 seemed (to me) to tip the balance in usefulness and cleanliness.
And with the idea of taking Elm straight to WASM (and with WASI around), things are looking interesting in the (eventual… gosh but it moves slowly) future.
tl;dr: It's absolutely worth taking a new look at Elm 19.
JS is a scripting language, good for small and quick scripts. Shouldn't be used for anything more than a few hundred LOC.
I wonder if picking a niche language would give me an advantage or there would be too few jobs.
now he alone does not make up the "community", but he's certainly a prominent member of it.
And the github forum rules are a little "threatening" (https://github.com/elm/forum-rules/blob/master/README.md).
This is FUD. Elm is being used pretty successfully in production.
This is not something you can afford to do with a long-running, critical app, or at least you have to know you're taking some risks. It's a tradeoff.
Also, what would happen to Elm if the author suddenly died?
I've worked with many languages, beginning with Fortran in my student days that I had to punch my own cards, then doing Basic, Pascal, C, C++, Python, Ruby, Javascript, Lisp, Scheme, some Haskell, not necessarily in that order. I've never had more fun and felt more productive than with Elm. The ability to do "extreme refactoring" and come out on the other side with everything working is liberating, exhilarating, and makes code maintainable and growable over the long term.
Been using it now for three years.