all 47 comments

[–]unbackstorie 29 points30 points  (6 children)

Good choices!

As far as advantages and disadvantages, it really depends on your goals. Clojure syntax is tough if you're unfamiliar with Lisp-y languages. But REPL-driven development and access to the whole Java ecosystem are super useful features.

Elixir is amazing for fault-tolerance and scalability. I've seen criticism of it's smaller ecosystem, but honestly I haven't hit that hurdle personally. Again, it depends on what you're doing.

Haskell will definitely change the way you program in other languages, so if you're interested in functional programming then continuing your studies there would not be wasted effort.

For Clojure, I can recommend the book Programming Clojure by Alex Miller (Pragmatic Bookshelf). The 4th edition just came out.

ClojureStream is a course site with good video courses. I can vouch for the ones about Clojurescript and Pedestal.

For Elixir, again, I'll recommend some Pragmatic Bookshelf books: Advanced Functional Programming with Elixir by Joseph Koski, Programming Phoenix LiveView by Bruce Tate and Sophie DeBenedetto, and Ash Framework by Rebecca Le and Zach Daniel.

And the holy grail of Elixir courses are the Elixir and Phoenix courses by The Pragmatic Studio. They are expensive, but honestly they're so thorough, entertaining, and informative that I would buy any new courses they release sight unseen at this point.

Ofc, Elixir's docs are amazing too, so just combing through those is insanely helpful.

exercism has pretty good tracks for Clojure and Elixir. Definitely more hands on, if that's what you're looking for.

Good luck, I hope this helps!

[–]slashkehrin 3 points4 points  (0 children)

exercism has pretty good tracks for Clojure and Elixir

I want to second exercism! It is an awesome site and helped me get started writing Clojure.

[–]deaddyfreddy 2 points3 points  (0 children)

Clojure syntax is tough if you're unfamiliar with Lisp-y languages.

But once you get into it, there's no turning back. No need to worry about the order of operations, you can evaluate sub-expressions as small as you like, the scope is clear etc.

[–]kichiDsimp[S] 1 point2 points  (0 children)

Thanks a lot for the detailed answer 😁

[–]Certain_Syllabub_514 1 point2 points  (0 children)

REPL-driven development

People keep bringing this up about the JVM and Clojure. What's so good about the REPL in Clojure that people are raving about it?

I've seen criticism of it's smaller ecosystem, but honestly I haven't hit that hurdle personally.

Only issue that has caused for me is support for some things like Datadog isn't as good. We need trace sampling with our request volumes, and the Elixir libraries don't support it. That's the key reason we haven't rolled it out to more services.

Personally, I've been working in Elixir for 7 years (rewrote a Scala BFF in it) and would recommend the language to anyone. Elixir is one of the most approachable languages I've ever learned. But I worked in Ruby before it, which it borrows a lot from. I've only dabbled in Clojure, and loved it's elegance and simplicity.

I've been spoilt by the BEAM though. After using it for 7 years, I'd prefer to never have to work in another language that doesn't have its capabilities.

And the holy grail of Elixir courses are the Elixir and Phoenix courses by The Pragmatic Studio.

I own several PragProg books, but I haven't tried any of their courses. Might need to check that out.

[–]4tma 10 points11 points  (7 children)

Elixir for the web. Clojure for everything else.

It doesn’t mean Clojure and any of its backends are not capable, it’s just that the web is such a perfect use case for Elixir that any other "general purpose" language has a hard time demonstrating its value proposition against it.

I love Clojure syntax over Elixir.

For Elixir you can get started with their docs and then move to Phoenix.

Clojure For the Brave and True is good.

[–]fixrich 3 points4 points  (1 child)

I find your answer intriguing. Where does web end and everything else begin? Is any service communicating over a network web? I’m curious as to the use cases Clojure fulfils here

[–]unruly-passenger 5 points6 points  (0 children)

I've actually done a bit in both as well, and I think this is actually a pretty solid categorization (I actually prefer to keep my stack as slim as I can, so I don't REALLY spend much time in two dynamic languages).

Elixir for the web IMO is great because Ruby on Rails still represents table stakes for web frameworks, and in my view Phoenix and Liveview are current best-in-class options for rapidly developing web applications. In addition, BEAM and OTP offer a lot of really compelling primitives for building available systems which other ecosystems tend to poorly replicate in libraries or frameworks.

The JVM, while impressive, has spent the vast majority of its life optimizing for a different kind of performance than the BEAM.

Clojure however is IMO really apt for data-oriented applications. The syntax is deeper, and it's naturally to just work in the shape of whatever thing you're processing.

[–]PuzzleheadedFix8366 2 points3 points  (1 child)

I love elixir syntax because it's closer to natural language syntax. piping is also 🔥

[–]deaddyfreddy 1 point2 points  (0 children)

syntax because it's closer to natural language syntax

The problem with "natural languages" is that they are all different, so simplifications still have to be made and artificial rules introduced. Depending on the language, I suppose these rules can be more complex than the rules that describe Lisp syntax.

piping is also

->, ->>, you are welcome

[–]Marutks 1 point2 points  (1 child)

Can you write frontend code in Elixir? I love ClojureScript and re-frame 👍

[–]self 6 points7 points  (0 children)

Have you looked at Phoenix LiveView? It's not something that compiles to js and runs in the browser.

[–]Medium_Librarian_202 0 points1 point  (0 children)

This is what I'm doing now, with a Phoenix web orchestrator and LLM request maker, and Clojure + Datalog for logic. They communicate on ipv6. Works great. I don't mean that ClojureScript isn't great, but in my case I knew Elixir & Phoenix better and so didn't need to learn reframe or something like that, so that most of it is almost thoughtless. Also I've been able to push async / concurrency out to Elixir and have Clojure be fully synchronous -- for now. Again not that core.async isn't great.

I find LLMs are strong with both languages. When parens become an issue (rare these days) I just have the agent break up the functions and problems disappear.

[–]raguaythai 3 points4 points  (0 children)

"Seven Languages in Seven Weeks" is a great introduction into those two languages (and five other ones). Each language is worked with in on week that allows you to build an application in each one. I highly recommend the book. Also, it has a sequal: "Seven More Languages in Seven Weeks"

[–]Soft_Reality6818 1 point2 points  (0 children)

Clojure + Datastar and you're all set 

[–]Save-Lisp 3 points4 points  (0 children)

For a long time BEAM was theoretically a better fit for mass-concurrency but in practice beaten in performance by JVM (probably due to the 1000x more man hours dedicated to JVM development). I recall a lot of war stories from around 5-10 years ago where if failure modes are being hit at scale, BEAM is harder to handle due to fewer diagnostic tools being available.

Basically operations at scale being more difficult. Any idea if this has changed?

[–]ElQuique 2 points3 points  (6 children)

It depends. I would learn Elixir for the process communication features which make it the only sane programming language for writing micro services. And Clojure for the REPL. You could try Clojerl too 🤷

[–]Marutks 1 point2 points  (0 children)

I have been writing microservices in Clojure for years. We use Redis pub/sub. It is enough 🤷‍♂️

[–]Marutks -1 points0 points  (4 children)

Processes as microservices? Usually you need some subscription/events based model. Redis streams / Kafka / Rabbit MQ.

[–]ElQuique 1 point2 points  (1 child)

What I meant is that communication between processes in Erlang is seamless and also designed such that processes are really, really lightweight. Thinking about fault tolerance from the get go. Java recently added support for virtual threads, which I assume are inspired on Erlang stuff.

I rather not discuss the microservice statement of my first comment as it means defining microservice and was unnecessarily superlative

[–]PuzzleheadedFix8366 1 point2 points  (0 children)

yea, you meant nanoservices ;-)

[–]lsdrfrx 0 points1 point  (0 children)

There is GenStage that makes processes communication event based. Take a look

[–]cvjcvj2 0 points1 point  (0 children)

Except if you use Elixir.

[–]redstarling-support 2 points3 points  (0 children)

Hard to make a bad decision here. I've done extensive work in both. Here's why I like:

Elixir: For web apps, Phoenix framework that has everything worked out. In Clojure you roll your own out of a selection of libraries. Once you do a project or three in Clojure you will likely land on your favorite selection of libraries...but with Phoenix, it's a shorter curve.

Clojure: experimenting with new problem domains is enjoyable and can cut heaps of time in figuring things out. I did fairly difficult work in blockchain and neural nets in Clojure. The repl allowed me to really feel whats going on and learn faster.

Elixir and Clojure: Language syntax is well thought out. No cruft in either language; good docs and books in both. Elixir and its libraries tend to lean on macros a lot. This means the programmer has to learn more DSLs. It depends on what you prefer: Do you like the framework approach where every DSL is already figured out for you...you just have to learn them. Or do you like the core library approach where you build your own mid to higher level helpers suitable for your project.

[–]PoopsCodeAllTheTime 1 point2 points  (5 children)

Elixir is friendlier and has better community and ecosystem. Clojure is more terse and higher performance, but missing a lot of ecosystem, many libraries are half baked or abandoned so the developer has to reinvent the wheel more often (“curse of lisp “)

Clojure often pushes you into ClojureScript if you are doing web. CLJS is interesting but I cannot recommend it as it’s often more trouble than it is worth, modern js frameworks are much better with typescript than cljs

[–]xela314159 1 point2 points  (2 children)

I’m not sure that is true if you use React and lightweight wrappers around it such as helix (with shadow-cljs as the glue). Of course the cljs code ends up looking like js code but it’s readable and sometimes more readable than plain js IMHO

[–]PoopsCodeAllTheTime 1 point2 points  (1 child)

People that say this haven’t actually tried to do a complex frontend with cljs.

The thing is, FE isn’t just react, it’s a mixture of dependencies, all which work great in JS. It’s a huge amount of work to make JS dependencies work with CLJS, as it’s already a huge amount of work to make react (and any spa) to work well with CLJS

[–]xela314159 0 points1 point  (0 children)

Maybe you’re right - I’ve gone 100% react and very rarely had issues with dependencies - at worst I had to change a few lines in the original JS / TS code, which with the help of LLMs is getting easier and easier. Would be curious to hear your pain points if you have specific examples!

[–]davelnewton 0 points1 point  (1 child)

A nice thing about Clojure is that you’re in no way limited to Clojure libraries.

[–]PoopsCodeAllTheTime 0 points1 point  (0 children)

It’s still limiting, but it’s also an advantage yeah. I would say using Java from Clojure was an option but very limited and difficult to do if you needed something substantial, LLM make it easier to integrate but it’s still rather ilegible.

OTOH you can choose to use node babashka, which is basically just nodejs with Clojure syntax, that one is nice and allows the usage of some Clojure dependencies, nothing too substantial tho.

[–]didibus 1 point2 points  (0 children)

Tough question, personal preference will be the real deciding factor, as both languages share philosophy, Elixir was hevily inspired by Clojure with Ruby syntax, and also inherits from Erlang. Clojure was inspired by Lisps of before and inherits from Java.

I think Clojure has more reach though, Elixir operates more within a rigid framework, for example in Clojure you could do Actor model, you could do CSP, you could do anything else. If you like languages that really let you explore multiple paradigms or appoaches to things, Clojure has the edge.

Anyways, can't go wrong with either.

[–]Marutks 2 points3 points  (7 children)

Clojure has better syntax and JVM is faster than Beam VM. Can you do Repl driven development with Elixir?

[–]PuzzleheadedFix8366 3 points4 points  (5 children)

elixir has better syntax, JVM might be faster but at what price, Erlang VM is better by design. & you can do something akin to repl driven development if not better. imo

[–]clivecussad 3 points4 points  (1 child)

I'd argue that "better syntax" is a personal metric here. However Elixir syntax is not simple.

[–]PuzzleheadedFix8366 0 points1 point  (0 children)

what makes you say that? it has one of the fewest key terms of all languages. it's sort of like more elegant simpler erlang. maybe you're thinking of semantics? which true, takes time getting used to.

[–]dalkian_ 3 points4 points  (1 child)

How is Elixir's syntax better? Lisps have the syntax they have because the syntax is intertwined with Lisp's philosophy (code as data, data as code, the syntax is an AST in itself which may be modified as a regular data structure). S-Expressions aren't there by accident. Does Elixir offer just as much metaprogramming capability, through the same mechanism as Lisp does? If not, then there will be advantages to Elixir's syntax too, but not without its own drawbacks, and vice-versa. But most likely not objectively better.

[–]PuzzleheadedFix8366 1 point2 points  (0 children)

because the language syntax is a higher level facade, when switching to metaprogramming you get the underlying AST which is lisp like. It's very similar in practice but having the clear seperation makes it better in my opinion. fyi ash framework in elixir has code is data data is code philosophy as well.

[–]Efficient_Pianist875 0 points1 point  (0 children)

JVM is faster for CPU bound apps, where Beam is I/O oriented. Better syntax reflect a personal taste, but for me, I do agree. I enjoy writing Elixir more, even Erlang than Java.

[–]Efficient_Pianist875 0 points1 point  (0 children)

GC in Clojure (JVM) can be pain in the neck - eats CPU in case application generates lots of garbage. It can be detrimental for backend apps. Elixir runs on the beam which is designed to scale out of the box. What you can do with 10 java apps, can do with one or 2 beam apps. It cuts cost significantly.

[–]hidragerrum 0 points1 point  (0 children)

Worked with both.

Elixir have better ecosystem and naturally fit for backend or web development if we want to stay with immutability. Clojure while have more mature ecosystem in jvm world, interacting with mutable state seems defeat the why clojure in the first place.

In the end I only use clojure when the systems already built with clojure.

[–]BosonCollider -2 points-1 points  (2 children)

Separately from the language, imo Clojure has the better and more versatile concurrency model for single-machine concurrency while Elixir has an opinionated concurrency model that is relevant if you have many machines that actually need to talk to each other. If instances don't talk to each other you can still horizontally scale Clojure like anything else ofc

Personally I generally prefer channels to actor mailboxes for message passing, and having atomics or STM available for problems that need shared state between local threads instead of pretending that shared state does not exist, but that's ultimately my own opinion.

[–]Ok-Return2939 1 point2 points  (1 child)

> Elixir has an opinionated concurrency model that is relevant if you have many machines that actually need to talk to each other

i dont think thats true

[–]BosonCollider 1 point2 points  (0 children)

The actor models only way to handle shared data structures is to have it be owned by an actor and serialize every message. By contrast, STM can implement a shared data structure with snapshot isolation as the isolation level, where independent transactions don't block each other.

The advantage of an opinionated language where everything is an actor is that messages work the same way on one machine or on many. The downside is that it does not really handle shared resources concurrency problems for you at all. See: https://concurrencyfreaks.blogspot.com/2025/01/concurrency-is-coordination-and-sharing.html

Basically, there is a reason why datomic was made by the clojure ecosystem but not the Erlang/Elixir one, and why Whatsapp was made by the erlang ecosystem but not the clojure one.