That was my first thought, too: Elm is quite similar to OCaml, so why not using OCaml in the backend? With all that server-side stuff around MirageOS, you can not only build a server app, but a full server VM within OCaml.
On the other hand, OCaml also targets JavaScript coding in the browser, via js_of_ocaml or the newer bucklescript.
I would be very interested in a comparison of client-side development with OCaml+js_of_ocaml (or OCaml+bucklescript) versus Elm.
From what I understand Elm is planned to target Node eventually. However the language is pretty young right now and the main work is going into making client side Elm the best it can be. The language author doesn't want to branch out too far.
It makes my heart sing that a different VM than JVM or .Net is gaining more traction, and as a static types fan it makes me happy that a statically typed language may be getting on Beam.
I think the compile target is basically not a big problem, and there's been explicit work to make the Browser just one Platform. Also, see the `Process` module in the core docs for a hint of the future, regardless of where it lands (this would also work well for WebWorkers etc, and is handled right now by a naive scheduler). Concern over the compile target should be super low - if you've built a language of sorts, you should have a feel for how it's not that big of a deal to tweak the target.
It will definitely be a very different compile target. But Elm really doesn't take any inspiration from Javascript, so I don't think it will change much about the fundamentals of the language. Of course they'll need to add capabilities for concurrency, OTP, etc.
Whereas after spending even a day with it I found myself wanting to run screaming back to Haskell.
No typeclasses, no monads, more primitive pattern matching, no guards, use of massive nested elseif considered idiomatic ...
Simple tasks wind up feeling tedious because so many of the standard tools and patterns of any other ML language are simple missing completely. The result feels like trying to work with one of those random toy Lisps people make in a weekend and throw up on Github.
It frustrates me, because in theory I do like the concept of the Elm architecture, I love ML-style type systems, and I work with reactive programming every day in ClojureScript. But the language it's attached to is so incomplete that it quickly just becomes a pain.
That's exactly the dividing line I see in those who love Elm and those who don't. People coming from Ruby / JS backgrounds appreciate a modern type system and it isn't so different from languages they are used to that it is hard to get going. People already used to modern functional programming miss the abstractions they are used to that Elm doesn't provide. I find this really interesting. As a community we don't give enough attention to the sociological aspects of programming. You can see similar things in the adoption of languages like Go and Elixir.
(Sorry for my poor english but i need some clarification) Do you put Go, Elixir and Elm is the same bag, i.e langages a step further JS in term of abstraction, i.e langages easy to understand when you come from plain JS? And then, you put Purescript and Haskell in another bag, i.e langages with advanced abstractions absent from "casual" langages, i.e langages requiring a lot of effort to learn? Did I summarize your idea correctly?
I think understood the Elm/Haskell/Purescript, i.e:
Javascript dev sees Elm and might think "Wow,a type-system and it is awesome, didn't expect that"
Haskell dev sees Elm and might think "I am missing so many features with these types" and consider something else, probably pure-script, that has similar toolbelt.
a-saleh is correct. Regarding Go and Elixir, I think Ruby / Python seem them and think "wow, I can program in a familiar style and I get performance and concurrency!" Haskell etc. people look at them and think "this smells."
Look no further! Try Purescript. It's basically Haskell (albeit strictly evaluated) and has everything you listed, plus a record system I really wish Haskell would adopt.
I'm currently using it in production with purescript-thermite, a thin wrapper over React, and it's worked out great.
Elm is great for people coming from JS, but not so much for people coming from Haskell.
You can get a "kind of Elm" experience on the backend with Flow and JavaScript / NodeJs. The type system of Flow is not as strict as Elm, but you have the main parts (including disjoint unions) and can use existing JavaScript code. With React and Redux you also get a similar framework for the frontend.
You are right about the type system but still, Elixir is a lighter, friendlier functional programming language compared to haskel or erlang. Not having previous FP experience, I'm having a good time learning Elixir + Elm.
Elixir and Erlang have a type checker as well. Not Hindley–Milner but one called "success typing" and is checked by a tool called Dialyzer. It is rather good actually:
But is still optional to compile. The more precise and better the type annotations, the more helpful it is. If it can deduce that some inconsistency or type error occurring it will let user know. If it is not sure, it won't say anything.
Another point is because of isolated process heaps and extra fault tolerance, it is possible to get a high degree of assurance from a system built in Erlang or Elixir even with dynamic typing. And there are certainly many examples of that.
Dialyzer is indeed nice, but as you say, it's completely optional, it's not Hindley-Milner, and it relies entirely on thorough, correct type annotations from the developer.
Elixir also follows the Ruby philosophy of hiding as much behind the syntax as possible, whereas Elm tends to be very explicit. So I stand by my point that Elixir is a very different language in practice from Elm.
I use Erlang and I like it a bit more than Elixir in general exactly because it is more explicit. It involves slightly more syntax but there is less hidden magic stuff happening behind the scene.
> Elixir also follows the Ruby philosophy of hiding as much behind the syntax as possible, whereas Elm tends to be very explicit.
This is not true. While Elixir may not make things as explicit as Elm (I don't know Elm well enough to assert or refute such a statement), the author of Elixir has stated in various occasions that Elixir prefers explicit to implicit.
For instance, in Elixir, unless one explicitly defines a String.Chars protocol for a data structure, string interpolation of that data structure will not compile. In contrast, pretty much everything can be implicitly interpolated in Ruby.
Another example is function calls. In Elixir, if f is not a named function but a variable bound to an anonymous function, you cannot call it by writing something like f(x). Instead, you must put a dot after f and write f.(x) to state clearly that you are calling an anonymous function.
Could you clarify? I don't immediately see the relevance of this. Why does it matter if the compilation target is or is not also strongly typed, and how does that imply anything about the experience of using a high-level dynamically typed language vs. a high-level statically typed language?
I understood the (some number of grands-)parent comment to be saying "Elixir seems a good candidate [for a compilation target]." If that was not your interpretation, then it is certainly non-sequiter. Re-reading, I think I misread something in a surrounding comment, though I'm not sure what.
I said "Elixir seems a good candidate" for backend development, not as a compilation target. The OP asked for suggestions regarding light, friendly FP languages for backend dev.
I have been thinking about toying with JVM's Graal compiler (JIT and customizeable deopts for the price of an interpreter). But with a language that has few corner cases. I was thinking maybe regexes, or brainfuck, or QBasic.
I'd just looove to try and put Elm on the JVM, but I just can't at the moment.
The reason I personally haven't looked at Scala (or Scala.js) is the OOP + FP approach.
<personal understanding>
Scala seems to want to please everyone, having all the tools at hand - Immutable and Mutable collections, Objects and enforced Pure Functions etc.
That to me seems like a call for trouble in the community, a split between approaches to problem solving at a fundamental level.
</personal understanding>
And well one of the things that I love about Elm is the lack of needing to make too many choices because the language doesn't have too many complicated features and prioritises "One good lib over 5 decent libs" (though at this young stage what other option does it have).
Clojurescript, Javascript and I'm betting Scala.js offer a lot of solutions to the same problem and/or have fancy language features that I am looking for an escape from. Just personally want to try going back to "less is more", to see what it is like. :)
Well, Scala was originally designed to complement a platform that encourages mutable data structures. When Scala was first getting started, Java didn't have anonymous functions yet, and it was a different time.
Currently, Scala-Native is working on off-JVM Scala, Scala.js is already plenty capable of compiling to efficient JS, and when wasm lands, it'll probably manage that rather easily.
Elm is a neat language, but I think it sort of presents too wide a divide. I have the same problem with Elm as I do with Typescript, Dart, and all of the other languages that recognize JS is a shit language, but just compile to it anyways.
Scala is a VERY well-designed language, and FP/OOP isn't nearly as polarizing as it might seem. I'd recommend LearnXinY Scala, read a little bit and you might see how natural it actually is (excluding the obnoxious syntax for certain things)
But yeah, JS has always been the crucial problem that needs to be solved. *.js is never going to solve it.
<opinion> OOP+FP is the wrong approach, and I'm putting that kindly. (This is also why I nixed Clojure, btw.) Having worked on large OO codebases, dependency hell, side effects, runaway state, and mutable data bugs (not to mention concurrency bugs) eventually become a serious problem, and out of the box, OOP does nothing to discourage these things (but functional languages do). As soon as you give developers access to "impurity" along with their "purity," you start earnestly down the path to massive technical debt. You could code the most functional Scala code ever, but as soon as you incorporate any library at all, you are right back to "zero concurrency guarantees" square 1.
What I'm getting at is that OOP+FP is, ultimately, simply OOP, providing none of the guarantees and encouraging none of the best practices that purely FP langs do. It's like the worst of both worlds lol
I think the important part is to think about how to properly modularize your codebase and not overdoing it.
That gets you an "internal" library ecosystem and not only ensures that incremental builds are fast (not really a concern anymore on nowadays) but also fast parallel compiles and deploys.
Code reviews are of course important so that developers are on the same page, but Scala is really great about isolating parts that have to be complicated from the rest (not like Java's " oh, you used a wildcard over there, let's infect the whole codebase with it").
So life is pretty good and I don't think there are huge issues you have to keep in mind compared to other languages, despite the goodness Scala provides you with.
I think most outside concerns are largely overblown: E. g. some people coming from Java might be excited in the first week bring able to define some operators, but it doesn't really matter. Most of the symbols you see in practice are pretty standard (+, -, ... for arithmetic, a few collection things like +
(add one thing), ++ (add multiple things) etc.).
Going overboard with it is considered bad style, so most libraries out there just don't do it anymore.
Tooling is pretty great overall, although IDEs could always be a bit better.
Compatibility is great and releases are rock-solid, even things like Scala.js which hasn't even reached 1.0 yet.
Scala.js has react[1] which combined with diode[2] (influenced by elm) creates a powerful framework for developing new generation of web apps. There's also a GraphQL implementation called Sangria[3].
For me it boils down to variant types (also known as Algebraic Data Types but I prefer the ML nomenclature) and the corresponding pattern matching that comes with it. This is IMO the major feature that makes FP languages so damn safe.
Everyone talks about higher order functions and generic types but lots of languages have that.... the really good ones have ADTs.
Oh what I would give to have ADTs in Java. I would stop using Exceptions for errors (not all exceptions are bad just most of them). I would stop using the annoying visitor pattern... That reminds me that I need to rexamine Derive4J
I had a similar epiphany when I started using Rust. I hadn't really understood the power of ADTs, having only come across them in Haskell, until I saw them in an otherwise more familiar language, and it totally changed the way I think about things.
That's true, but after coming from Laravel / PHP stack I don't take it as huge disadvantage as it was in Laravel. First, because each new breaking feature comes with a dozen of articles covering changes and how-to. Secondly, I am person who likes discovering, learning, playing with all these new features, I don't complain to learn something new. And last argument is that each new version comes is way more developer-friendly than previous release.
I wonder about the ecosystem of any advanced language running on a JS engine.
Basically, two things may happen in the future:
- all the libs existing in JS are recoded by dedicated fanatics in the advanced language. Then you have a versatile all-purpose development ecosystem with this language.
or
- you rely upon the existing JS ecosystem and libs, and eventually spend your time struggling with the impedance mismatch between JS and your advanced langage.
Did you feel something like that with Purescript, Elm, etc?
I think one of the primary benefits to ELM, etc is the typechecking, which you lose if you use FFI to call outside JS.
Once you dont need jquery and you actually have a standard library, 2 of the largest requirements to the existing ecosystem (JQuery and Underscore.js) are no longer needed.
Elm looks to handle much of he DOM and events so I doubt you would need React.js or Angular, which just leaves your datastore.
(to better put it, much of the packages in Javascript are there because the language itself is insufficient to describe DOM interactions. Elm was created explicitly for the web thus removes the need for many packages)
The other issue is the untyped nature of JSON.
Interacting with JSON from Elm does require a lot of Encoder/Decoder.
May be JSON/LD could be a nice improvement for that, but it will require a significant effort to be broadly adopted.
I haven't used Elm, but generally I think it shouldn't be that scary to use an "advanced language" if it has an even remotely decent FFI.
It might seem like horror to have to interface to external dependencies, but if the libraries you want to access aren't extremely complex then interfacing with them probably isn't going to be such a huge problem.
In some cases you might want to use the advanced language for some things but keep the outer layer of the app in JavaScript. At a previous workplace we had a particularly tricky parsing module written in Fay (Haskell subset compiling to JS).
The latter case has worked out great for us at helme.io. Our computational forecast library is written in Clojurescipt, while the rest of the application is in JS + Angular.
A word of warning: with Clojurescript, at least, there is a big cost to converting between JS and CLJS types. This has been a big painpoint as our need for frequent calculations increases. Still, we wouldn't have it any other way.
70 comments
[ 3.1 ms ] story [ 131 ms ] threadI didn't think I needed types, until I tried Elm, now I am seriously having an identity crisis..
All my backend Python code is also riddled with type hints to help out with the cognitive load.
Elm - Simple, friendly, helpful
Just wish Elm was more mature, but loving it anyway!
On the other hand, OCaml also targets JavaScript coding in the browser, via js_of_ocaml or the newer bucklescript.
I would be very interested in a comparison of client-side development with OCaml+js_of_ocaml (or OCaml+bucklescript) versus Elm.
You'd think Beam would be a very different compile target than JS, and it seems weird to make such a huge change without very good reason.
The Elm folks have been fairly buddy-buddy with the Elixir/Phoenix folks, so this doesn't surprise me
It makes my heart sing that a different VM than JVM or .Net is gaining more traction, and as a static types fan it makes me happy that a statically typed language may be getting on Beam.
http://i.imgur.com/pqk9oMl.png
It will definitely be a very different compile target. But Elm really doesn't take any inspiration from Javascript, so I don't think it will change much about the fundamentals of the language. Of course they'll need to add capabilities for concurrency, OTP, etc.
No typeclasses, no monads, more primitive pattern matching, no guards, use of massive nested elseif considered idiomatic ...
Simple tasks wind up feeling tedious because so many of the standard tools and patterns of any other ML language are simple missing completely. The result feels like trying to work with one of those random toy Lisps people make in a weekend and throw up on Github.
It frustrates me, because in theory I do like the concept of the Elm architecture, I love ML-style type systems, and I work with reactive programming every day in ClojureScript. But the language it's attached to is so incomplete that it quickly just becomes a pain.
Javascript dev sees Elm and might think "Wow,a type-system and it is awesome, didn't expect that"
Haskell dev sees Elm and might think "I am missing so many features with these types" and consider something else, probably pure-script, that has similar toolbelt.
Not sure about the rest though.
I'm currently using it in production with purescript-thermite, a thin wrapper over React, and it's worked out great.
Elm is great for people coming from JS, but not so much for people coming from Haskell.
http://learnyousomeerlang.com/dialyzer
But is still optional to compile. The more precise and better the type annotations, the more helpful it is. If it can deduce that some inconsistency or type error occurring it will let user know. If it is not sure, it won't say anything.
Another point is because of isolated process heaps and extra fault tolerance, it is possible to get a high degree of assurance from a system built in Erlang or Elixir even with dynamic typing. And there are certainly many examples of that.
Elixir also follows the Ruby philosophy of hiding as much behind the syntax as possible, whereas Elm tends to be very explicit. So I stand by my point that Elixir is a very different language in practice from Elm.
This is not true. While Elixir may not make things as explicit as Elm (I don't know Elm well enough to assert or refute such a statement), the author of Elixir has stated in various occasions that Elixir prefers explicit to implicit.
For instance, in Elixir, unless one explicitly defines a String.Chars protocol for a data structure, string interpolation of that data structure will not compile. In contrast, pretty much everything can be implicitly interpolated in Ruby.
Another example is function calls. In Elixir, if f is not a named function but a variable bound to an anonymous function, you cannot call it by writing something like f(x). Instead, you must put a dot after f and write f.(x) to state clearly that you are calling an anonymous function.
I'd just looove to try and put Elm on the JVM, but I just can't at the moment.
I kind of like to think of it as the Java to late 90s' C++, where "less is more" made it popular.
<personal understanding> Scala seems to want to please everyone, having all the tools at hand - Immutable and Mutable collections, Objects and enforced Pure Functions etc.
That to me seems like a call for trouble in the community, a split between approaches to problem solving at a fundamental level. </personal understanding>
And well one of the things that I love about Elm is the lack of needing to make too many choices because the language doesn't have too many complicated features and prioritises "One good lib over 5 decent libs" (though at this young stage what other option does it have).
Clojurescript, Javascript and I'm betting Scala.js offer a lot of solutions to the same problem and/or have fancy language features that I am looking for an escape from. Just personally want to try going back to "less is more", to see what it is like. :)
Currently, Scala-Native is working on off-JVM Scala, Scala.js is already plenty capable of compiling to efficient JS, and when wasm lands, it'll probably manage that rather easily.
Elm is a neat language, but I think it sort of presents too wide a divide. I have the same problem with Elm as I do with Typescript, Dart, and all of the other languages that recognize JS is a shit language, but just compile to it anyways.
Scala is a VERY well-designed language, and FP/OOP isn't nearly as polarizing as it might seem. I'd recommend LearnXinY Scala, read a little bit and you might see how natural it actually is (excluding the obnoxious syntax for certain things)
But yeah, JS has always been the crucial problem that needs to be solved. *.js is never going to solve it.
What I'm getting at is that OOP+FP is, ultimately, simply OOP, providing none of the guarantees and encouraging none of the best practices that purely FP langs do. It's like the worst of both worlds lol
</opinion>
"OOP" implies none of things you are worried about and Scala is a great example of that.
I wished people tried the language before making assumptions based on emotions that are not supported by reality.
I think the important part is to think about how to properly modularize your codebase and not overdoing it. That gets you an "internal" library ecosystem and not only ensures that incremental builds are fast (not really a concern anymore on nowadays) but also fast parallel compiles and deploys.
Code reviews are of course important so that developers are on the same page, but Scala is really great about isolating parts that have to be complicated from the rest (not like Java's " oh, you used a wildcard over there, let's infect the whole codebase with it").
So life is pretty good and I don't think there are huge issues you have to keep in mind compared to other languages, despite the goodness Scala provides you with.
I think most outside concerns are largely overblown: E. g. some people coming from Java might be excited in the first week bring able to define some operators, but it doesn't really matter. Most of the symbols you see in practice are pretty standard (+, -, ... for arithmetic, a few collection things like + (add one thing), ++ (add multiple things) etc.). Going overboard with it is considered bad style, so most libraries out there just don't do it anymore.
Tooling is pretty great overall, although IDEs could always be a bit better.
Compatibility is great and releases are rock-solid, even things like Scala.js which hasn't even reached 1.0 yet.
1. https://github.com/japgolly/scalajs-react
2. https://github.com/ochrons/diode
3. http://sangria-graphql.org
Everyone talks about higher order functions and generic types but lots of languages have that.... the really good ones have ADTs.
Oh what I would give to have ADTs in Java. I would stop using Exceptions for errors (not all exceptions are bad just most of them). I would stop using the annoying visitor pattern... That reminds me that I need to rexamine Derive4J
[1] http://elm-lang.org
[2] https://en.m.wikipedia.org/wiki/Elm_(email_client)
[1] http://www.ibiblio.org/harris/500milemail.html
Basically, two things may happen in the future:
- all the libs existing in JS are recoded by dedicated fanatics in the advanced language. Then you have a versatile all-purpose development ecosystem with this language.
or
- you rely upon the existing JS ecosystem and libs, and eventually spend your time struggling with the impedance mismatch between JS and your advanced langage.
Did you feel something like that with Purescript, Elm, etc?
Once you dont need jquery and you actually have a standard library, 2 of the largest requirements to the existing ecosystem (JQuery and Underscore.js) are no longer needed.
Elm looks to handle much of he DOM and events so I doubt you would need React.js or Angular, which just leaves your datastore.
(to better put it, much of the packages in Javascript are there because the language itself is insufficient to describe DOM interactions. Elm was created explicitly for the web thus removes the need for many packages)
It might seem like horror to have to interface to external dependencies, but if the libraries you want to access aren't extremely complex then interfacing with them probably isn't going to be such a huge problem.
In some cases you might want to use the advanced language for some things but keep the outer layer of the app in JavaScript. At a previous workplace we had a particularly tricky parsing module written in Fay (Haskell subset compiling to JS).
A word of warning: with Clojurescript, at least, there is a big cost to converting between JS and CLJS types. This has been a big painpoint as our need for frequent calculations increases. Still, we wouldn't have it any other way.
- continuous evolution of the JS ecosystem towards a cleaner language, with solutions like Flow or TypeScript.